您的位置:首页 > 编程语言 > Java开发

Eclipse3.1中体验J2SE 5.0之枚举类型

2007-03-19 14:46 465 查看
  本文将介绍J2SE5.0中三个比较重要的特性:枚举类型,注释类型,范型,并在此基础上介绍在如何在Eclipse3.1开发环境中开发枚举类型,注释类型和范型应用。  J2SE5.0(Tiger)的发布是Java语言发展史上的一个重要的里程碑,是迄今为止在java编程方面所取得的最大进步.
  J2SE5.0提供了很多令人激动的特性.这些特性包括范型(generics)的支持,枚举类型(enumeration)的支持,元数据(metadata)的支持,自动拆箱(unboxing)/装箱(autoboxing),可变个数参数(varargs),静态导入(staticimports),以及新的线程架构(ThreadFramework).
  随着J2SE5.0的推出,越来越多的集成开发环境(IDE)支持J2SE5.0的开发.著名的开源javaIDEEclipse从3.1M4开始支持J2SE5.0的开发,目前最新的版本是3.1RC4.
  本系列将介绍J2SE5.0中三个比较重要的特性:枚举类型,注释类型,范型,并在此基础上介绍在如何在Eclipse3.1开发环境中开发枚举类型,注释类型和范型应用.本文将介绍枚举类型.
  1.枚举类型
  1.1枚举类型简介

  J2SE5.0以及之前的JDK有两种基本方法可以来定义新类型:通过Classes以及Interface.对于大部分面向对象编程来说,这两种方法看起来似乎足够了.但是在一些特殊情况下,这些方法就不适合.例如,我们想定义一个类型Priority,它只能接受High,Medium,Low三种值.其他任何值都是非法的.J2SE5.0以前的JDK是可以构造这种类型的,但是需要做很多工作,有可能会带来如不安全(类型安全性问题???)等潜在问题,而J2SE5.0的枚举类型(Enum)能避免这些问题.
  Eclipse是java程序员最常用的开发平台,而Eclipse3.1提供对J2SE5.0的支持,它为J2SE5.0的新功能提供了帮助工具.在对枚举类型的支持上,它不仅提供了枚举类型的创建模板,而且为枚举类型的各种开发错误提供错误提示及帮助修改.
  本文首先介绍枚举类型的创建基本概念以及如何在Eclipse3.1平台上创建枚举类型,然后我们通过在Eclipse3.1开发环境中的例子来说明枚举类型的应用.
  1.2创建枚举类型

  下面的例子显示了如何创建一个最基本的枚举类型:
  清单1.枚举类型的定义


publicenumPriority{High,Medium,Low};

  它包括一个关键字enum ,一个新枚举类型的名字Priority以及为Priority定义的一组值.
  在Eclipse3.1平台上,按照下面步骤来生成枚举类型:(Eclipse3.1提供了一个新的枚举类型创建向导(wizard)以方便用户创建枚举类型)
  1)File->New->Other,模板列表显示出来.
  2)在模板列表上选中java->Enum,点击Next按钮
  3)按图1填写每一个域如下:
  1:Eclipse3.1枚举类型创建模板



  4)点击Finish按钮,生成Priority的类(定义???),并声明Priority的每一个值,如下图2所示:(High,Medium,low从何而来???)
  2:枚举类型Priority



  在创建枚举类型时,注意几个重要的概念.
XMLns:xsi="http://www.w3.org/2001/XMLSchema-instance">
·所有创建的枚举类型都扩展于java.lang.Enum.Enum是在J2SE5.0里定义的一个新类,它本身不是枚举类型.在创建枚举类型时,必须用enum关键字,不能直接地定义一个继承Enum的类来创建一个枚举类型,尽管所有创建的枚举类型实际上都是Enum的子类.如果直接继承Enum,compiler就会报错(会导致编译错误).如图3所示
3.直接继承Enum



·枚举类型里定义的每一个值都是枚举类型的一个实例,比方说High是Priority的一个实例.枚举类型又是扩展于Enum.所以枚举类型的每一个值声明时,缺省时都将映射到Enum(Stringname,intordinal)构造函数中.换句话说,enumPriority{High,Medium,Low}的实现是调用了下面的Enum构造函数:
清单2映射的构造函数调用


newEnum<Priority>("High",0);

newEnum<Priority>("Medium",1);

newEnum<Priority>("Low",2);

  每一个创建的枚举类型都是Enum的子类,除了上面调用父类Enum的构造函数外,枚举类型可以使用参数为定义一些自己的构造函数.当声明值时,只需调用此枚举类型定义的构造函数,而且不必添加new关键字.在清单3里,Priority的一个实例生成,这个实例就是High(38).

清单3.其它构造函数调用


enumPriority{

High(38),

Medium(36.5),

Low(5.2);

doubletemperature;

Priority(doublep)

temperature=p;

}

  另外要强调的两点:一是这些枚举类型的构造函数都是私有的.它是不能被其它的类或者其它的枚举类型调用的.而且这个私有修饰符是由编译器自动加的,如果我们定义这些构造函数时,在前面加上public修饰符,就会导致编译错误,如下图5所示.二是变量定义必须在枚举类型值定义之后.上图中doubletemperature必须在枚举类型值定义完了(分号表示枚举类型值定义完了,如Low(5.2);)才能声明.
4.枚举类型的构造函数是私有的



·在J2SE5.0以前,当我们实现一个枚举类时,一般都是把一个整数关联到此枚举类的某一个值的名字,出现的问题是同一个整数可以代表不同枚举类的值.下面的例子里定义两个枚举类CourseandGrade如下:

清单4.


publicclassCourse{

publicstaticfinalintEnglishLit=1;

publicstaticfinalintCalculus=2;

publicstaticfinalintMusicTheory=3;

publicstaticfinalintMusicPerformance=4;

}

publicclassGrade{

publicstaticfinalintA=1;

publicstaticfinalintB=2;

publicstaticfinalintC=3;

publicstaticfinalintD=4;

publicstaticfinalintF=5;

publicstaticfinalintINCOMPLETE=6;

}

如果开发者误把student1.assignGrade(Grade.A)写成student1.assignGrade(Course.EnglishList); 在编译阶段是不能发现问题的,如果用J2SE5.0枚举类型(enum)可以避免这些问题.

枚举类型每一个值都是public,staticandfinal的.也就是说,这些值是唯一的而且一旦定义了是不能被重写或修改.而且尽管在枚举类型每一个值声明时没有出现static关键字,实际上值都是静态的,而且我们不能在值前面加上static,public,final修饰符,否则就会出现下图6的错误.

5枚举类型值的错误声明



枚举类型都实现了java.lang.Comparable,枚举类型的值是可以比较排序的,排列顺序就是枚举类型定义这些值的顺序.  

1.3枚举类型的应用

  下面各小节介绍了枚举类型的各种应用.

 XMLns:xsi="http://www.w3.org/2001/XMLSchema-instance">1.3.1循环(Iteration
  当我们写程序时,常常遇到对数组或列表里的每一个对象进行处理的情况.在J2SE5.0以前,如果要在一个数组或列表里进行轮循时,我们的做法比较繁琐,需要借助Java.util.Iterator类,如下所示:
  清单5


Listpriorities=Priority.values().;

for(Iteratoriter=priorities.iterator();iter.hasNext();){

Priorityp=(Priority)iter.next();

process(p);

}

  现在我们可以通过J2SE5.0的for/inloop和枚举类型一起使用.这能使以前花很多时间写的程序简单化,如上面清单5的程序可简化为:
  清单6


for(Priorityg:Priority.values()){

process(g);

}

  我们把上面的伪代码写成程序在Eclipse3.1上运行,如下图所示,在右下控制平台视图里显示了运行结果.如果看不见控制平台,点击Window->OtherViews->Console,控制平台就会出现在右下角.
  6枚举类型在循环中的应用



  我们在使用for/inloop时要求它的表达式要求必须是数组或者是实现了java.lang.Iterable的集合,而枚举类型的values()函数返回的就是一个数组.另外循环变量的声明必须是在loop里,包括变量类型和变量名.
  我们不能在循环里使用一个在循环之外声明的变量.这和J2SE5.0以前forloop里用的循环变量的声明不同.
  1.3.2转换(Switch
  我们常用的一种判断语句就是Switch-case语句.在Switch语句中使用枚举类型,不仅能简化程序,而且增强了程序的可读性.
  清单8.


File1:Task.java

publicclassTask{

PrioritymyPriority;

publicTask(Priorityp){

myPriority=p;

}

publicPrioritygetPriority(){

returnmyPriority;

}}


File2:TestSwitch.java

publicclassTestSwitch(

Tasktask=newTask(Priority.Medium);

switch(task.getPriority()){

caseHigh:

//docaseHigh

break;

caseMidum://fallthroughtoLow

caseLow:

//docaseLow

break;

default:thrownewAssertionError("Unexpectedenumeratedvalue!");

}

}

  在Switch语句里使用枚举类型时,一定不能在每一个枚举类型值的前面加上枚举类型的类名,否则编译器就会报错(会导致编译错误).我们把上面的程序稍作修改,在case语句里加上枚举类型的类名并运行在Eclipse3.1平台上.我们发现Eclipse的问题视图里提示case语句里枚举类型值的前面加上枚举类型的类名是错误的,如下图8所示.
  图7:case语句里枚举类型的值



  原因是J2SE5.0的实现要求case语句里每一个枚举类型值是不能有枚举类型类作为前缀的.前面谈到过每一个枚举类型的值都是枚举类型的一个实例.那么当编译器编译case语句时,是如何处理这些实例的?这有两种情况:如果switch与枚举类型定义在同一个编译单元,第一次编译时一个新表会创建在内存里.在这个表里,每一个枚举类型的值都和它在枚举类型里定义的顺序关联起来.编译器编译结果就和下面清单9显示的的程序很像.只不过顺序号没有加到程序里,而是编译器在表里快速查询.如果枚举类型被修改或从定义,表会被更新.
  清单9:


publicclassTestSwitch(

Tasktask=newTask();

switch(task.getPriority()){

case0:

//docaseHigh

break;

case1://fallthroughtoLow

case2:

//docaseLow

break;

default:thrownewAssertionError("Unexpectedenumeratedvalue!");

}

}

  还有一种经常出现的情况是switch与枚举类型定义不是在同一个编译单元.在这种情况下,大多数编译器就会把switch-case语句翻译成一系列的if/else语句:
  清单10:


Prioritytmp=task.getPriority();

if(tmp==High)

//docaseHigh

elseif(tmp==Midium)

elseif(tmp==Low)

//docaseLow

else{

thrownewAssertionError("Unexpectedenumeratedvalue!");

}

  1.3.3MapsofEnumandSetsofEnum
  在J2SE5.0的java.util程序包中提供两个新类:EnumMap和EnumSet,这两个类与枚举类型的结合应用可使以前非常繁琐的程序变得简单方便.EnumMap类提供了java.util.Map接口的一个特殊实现,该接口中的键(key)是一个枚举类型.
  清单11:.EnumMap例子


publicvoidtest()throwsIOException{

EnumMap<Priority,String>descriptionMessages=

newEnumMap<Priority,String>(Priority.class);

descriptionMessages.put(Priority.High,"Highmeans...");

descriptionMessages.put(Priority.Medium,"Mediumrepresents...");

descriptionMessages.put(Priority.Low,"Lowmeans...");

for(Priorityp:Priority.values()){

System.out.println("Forpriority"+p+",decriptionis:"+

descriptionMessages.get(p));

}

}

  EnumSet类提供了java.util.Set接口的实现,该接口保存了某种枚举类型的值的集合.EnumSet的作用类似于特性的集合,或者类似于某个枚举类型的所有元素的值的子集.EnumSet类拥有一系列的静态方法,可以用这些方法从枚举类型中获取单个元素或某些元素,下面的程序例子显示如何这些静态方法:
  清单12:.EnumSet例子


publicclassTestEnumSet{

publicenumColorFeature{

RED,BLUE,GREEN,YELLOW,BLACK

};

publicstaticvoidmain(String[]args){

EnumSetallFeatures=EnumSet.allOf(ColorFeature.class);

EnumSetwarmColorFeatures=EnumSet.of(ColorFeature.RED,

ColorFeature.YELLOW);

EnumSetnon_warmColorFeatures=EnumSet.complementOf(warmColorFeatures);

EnumSetnotBlack=EnumSet.range(ColorFeature.RED,ColorFeature.YELLOW);


for(ColorFeaturecf:ColorFeature.values()){

if(warmColorFeatures.contains(cf)){

System.out.println("warmColor"+cf.name());

}

if(non_warmColorFeatures.contains(cf)){

System.out.println("non_WarmColor"+cf.name());

}

}

}

}


  我们在Eclipse3.1环境中运行上面的程序,结果如下图:
  8:EnumSet样例运行结果



  1.3.4枚举类型的函数定义
  在介绍创建枚举类型中曾提到枚举类型都是java.lang.Enum的子类.也就是说,枚举类型都是可编译的java的类,那么就可以在枚举类型里添加构造函数和其它函数,如清单13里的getDescription()
  清单13:


publicenumColorFeature{

RED(0),

BLUE(0),

GREEN(300),

YELLOW(0),

BLACK(0);


/**Thedegreeforeachkindofcolor*/

privateintdegree;

ColorFeatures(intdegree){

this.degree=degree;

}

publicintgetDegree(){

returndegree;

}

publicStringgetDescription(){

switch(this){

caseRED:return"thecolorisred";

caseBLUE:return"thecolorisblue";

caseGREEN:return"thecolorisgreen";

caseBLACK:return"thecolorisblack";

caseYELLOW:return"thecolorisyellow"

default:return"UnknownColor";

}

}}


  枚举类型的函数定义的应用是很有用的,例如可以让多个枚举类型实现同一个interface来达到程序设计的模式化.例如一个定义了getDescription()接口的interface,让有同样需求的不同枚举类型来实现它.上面的colorFeature可以实现它,另一个FontFeature也可以实现它.
  1.3.5特定于常量的类主体
  在上一节里提到枚举类型可以定义自己的函数,其实更进一步,枚举类型的每一个值都可以实现枚举类型里定义的抽象函数,这听起来很不可思议,我们可以先看下面的例子.


publicenumPriorityimplementsFeature{

High(38){

publicvoidperform(){

System.out.println("high38");

}

},

Medium(36.5){

publicvoidperform(){

System.out.println("medium36.5");

}

},

Low(5.2){

publicvoidperform(){

System.out.println("low5.2");

}

};

publicabstractvoidperform();

publicStringgetDescription(Priorityp){

returnnull;

}

}

  枚举类型Priority定义了一个抽象函数perform(),Priority的每一个值都对perform函数实现了重载,这就是枚举类型的特定于常量的类主体.在这种情况下,每声明一个值,枚举类型的一个子类生成,然后生成这个子类的唯一的实例来表示这个值.不同的值,就有对应的不同的子类.每个子类可以对父类的抽象函数进行重载.我们可以用下面的程序在Eclipse3.1环境中运行来证明此时3个子类生成.


publicclassTask{

PrioritymyPriority;

publicTask(Priorityp){

myPriority=p;

}

publicPrioritygetPriority(){

returnmyPriority;

}

publicvoidtest()throwsIOException{

if(myPriority==Priority.High)

System.out.println(Priority.High.getClass().getName());

if(myPriority==Priority.Medium)

System.out.println(Priority.Medium.getClass().getName());

if(myPriority==Priority.Low)

System.out.println(Priority.Low.getClass().getName());

}}

publicclassTestSwitch{

publicstaticvoidmain(String[]args){

Tasktask=newTask(Priority.High);

Tasktask1=newTask(Priority.Medium);

Tasktask2=newTask(Priority.Low);

try{

task.test();

task1.test();

task2.test();

}catch(IOExceptione){

e.printStackTrace();

}

}

  运行结果如下图10.
  9测试特定于常量的类主体运行结果



  由于特定于常量的类主体难理解容易出错,它的应用比较少,大多数情况下可以用switch-case语句替代.在这里简单介绍,仅供参考.
  1.4枚举类型的小结

  使用枚举类型是很简单的.它定义一个固定的、封闭的值集合,然后,在需要这些值中的某一个值时,可以通过它的名称来指定它,这就是枚举类型的简单性.枚举类型的值就是枚举类型的实例,编译器会确保没有传入其他的类型,这就是枚举类型的安全性.这些枚举类型就是类本身,因此,可以对类进行的所有操作同样可以作用于枚举类型上.我们要小心使用构造函数和函数重载方法,不要因为这些特性可用就任意使用它们.比如特定于常量的类主体,大多情况下可以用Switch语句来代替,更容易让人理解而且不容易出错.我们也看到了Eclipse3.1平台对枚举类型的支持,包括提供创建模板,错误信息提示等.总之,枚举类型的灵活应用能极大的方便和简化了我们的开发工作。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: