本文将介绍
J2SE 5.0
中三个比较重要的特性
:
枚举类型
,
注释类型
,
范型
,
并在此
基础
上介绍在如何在
Eclipse 3.1
开发环境中开发枚举类型
,
注释类型和范型应用。
J2SE 5.0 (Tiger)
的发布是
Java
语言发展史上的一个重要的里程碑
,
是迄今为止在
java
编程方面所取得的最大进步
.
J2SE 5.0
提供了很多令人激动的特性
.
这些特性包括范型
(generics)
的支持
,
枚举类型
(enumeration)
的支持
,
元数据
(metadata)
的支持
,
自动拆箱
(unboxing)/
装箱
(autoboxing),
可变个数参数
(varargs),
静态导入
(static imports),
以及新的
线程
架构
(Thread Framework).
随着
J2SE 5.0
的推出
,
越来越多的集成开发环境
(IDE)
支持
J2SE 5.0
的开发
.
著名的开源
java IDE Eclipse
从
3.1M
4
开始支持
J2SE 5.0
的开发
,
目前最新的版本是
3.1RC4.
本系列将介绍
J2SE 5.0
中三个比较重要的特性
:
枚举类型
,
注释类型
,
范型
,
并在此基础上介绍在如何在
Eclipse 3.1
开发环境中开发枚举类型
,
注释类型和范型应用
.
本文将介绍枚举类型
.
1.
枚举类型
1.1
枚举类型简介
J2SE 5.0 以及之前的 JDK 有两种基本方法可以来定义新类型:通过 Classes 以及 Interface. 对于大部分面向对象编程来说,这两种方法看起来似乎足够了 . 但是在一些特殊情况下,这些方法就不适合 . 例如,我们想定义一个类型 Priority , 它只能接受 High, Medium, Low 三种值 . 其他任何值都是非法的 .J2SE 5.0 以前的 JDK 是可以构造这种类型的,但是需要做很多工作,有可能会带来如不安全 ( 类型安全性问题 ???) 等潜在问题,而 J2SE 5.0 的枚举类型( Enum ) 能避免这些问题 .
J2SE 5.0 以及之前的 JDK 有两种基本方法可以来定义新类型:通过 Classes 以及 Interface. 对于大部分面向对象编程来说,这两种方法看起来似乎足够了 . 但是在一些特殊情况下,这些方法就不适合 . 例如,我们想定义一个类型 Priority , 它只能接受 High, Medium, Low 三种值 . 其他任何值都是非法的 .J2SE 5.0 以前的 JDK 是可以构造这种类型的,但是需要做很多工作,有可能会带来如不安全 ( 类型安全性问题 ???) 等潜在问题,而 J2SE 5.0 的枚举类型( Enum ) 能避免这些问题 .
Eclipse
是
java
程序员最常用的开发平台,而
Eclipse 3.1
提供对
J2SE 5.0
的支持,它为
J2SE 5.0
的新功能提供了帮助工具
.
在对枚举类型的支持上,它不仅提供了枚举类型的创建模板,而且为枚举类型的各种开发错误提供错误提示及帮助修改
.
本文首先介绍枚举类型的创建基本概念以及如何在
Eclipse 3.1
平台上创建枚举类型,然后我们通过在
Eclipse 3.1
开发环境中的例子来说明枚举类型的应用
.
1.2
创建枚举类型
下面的例子显示了如何创建一个最基本的枚举类型:
下面的例子显示了如何创建一个最基本的枚举类型:
清单
1.
枚举类型的定义
|
它包括一个关键字
enum
,一个新枚举类型的名字
Priority
以及为
Priority
定义的一组值
.
在
Eclipse 3.1
平台上,按照下面步骤来生成枚举类型:
(Eclipse 3.1
提供了一个新的枚举类型创建向导
(wizard)
以方便用户创建枚举类型
)
1) File->New->Other,
模板列表显示出来
.
3)
按图
1
填写每一个域
如下
:
图
1: Eclipse 3.1
枚举类型创建模板
4)
点击
Finish
按钮
,
生成
Priority
的类
(
定义
???),
并声明
Priority
的每一个值
,
如下图
2
所示
:(High, Medium, low
从何而来
???)
图
2:
枚举类型
Priority
在创建枚举类型时,注意几个重要的概念
.
XMLns:xsi="http://www.w3.org/2001/XMLSchema-instance">
·
所有创建的枚举类型都扩展于
java.lang.Enum. Enum
是在
J2SE 5.0
里定义的一个新类,
它本身不是枚举类型
.
在创建枚举类型时,必须用
enum
关键字,不能直接地定义一个继承
Enum
的类来创建一个枚举类型,尽管所有创建的枚举类型实际上都是
Enum
的子类
.
如果直接继承
Enum
,
compiler
就会报错
(
会导致编译错误
).
如图
3
所示
图
3.
直接继承
Enum
类
·
枚举类型里定义的每一个值都是枚举类型的一个实例
,
比方说
High
是
Priority
的一个实例
.
枚举类型又是扩展于
Enum.
所以枚举类型的每一个值声明时,
缺省时都将映射到
Enum(String name, int ordinal)
构造函数中
.
换句话说,
enum Priority {High, Medium, Low }
的实现是调用了下面的
Enum
构造函数:
清单
2
映射的构造函数调用
|
每一个创建的枚举类型都是
Enum
的子类,除了上面调用父类
Enum
的构造函数外,枚举类型可以使用参数为定义一些自己的构造函数
.
当声明值时,只需调用此枚举类型定义的构造函数,而且不必添加
new
关键字
.
在清单
3
里,
Priority
的一个实例生成,这个实例就是
High
(
38
)
.
清单 3. 其它构造函数调用
|
另外要强调的两点
:
一是这些枚举类型的构造函数都是私有的
.
它是不能被其它的类或者其它的枚举类型调用的
.
而且这个私有修饰符是由编译器自动加的
,
如果我们定义这些构造函数时
,
在前面加上
public
修饰符
,
就会导致编译错误
,
如下图
5
所示
.
二是变量定义必须在枚举类型值定义之后
.
上图中
double temperature
必须在枚举类型值定义完了
(
分号表示枚举类型值定义完了,
如
Low(5.2);)
才能声明
.
图
4.
枚举类型的构造函数是私有的
·
在
J2SE 5.0
以前,当我们实现一个枚举类时,一般都是把一个整数关联到此枚举类的某一个值的名字,出现的问题是同一个整数可以代表不同枚举类的值
.
下面的例子里定义两个枚举类
Course and Grade
如下:
清单 4.
|
如果开发者误把
student1.assignGrade(Grade.A)
写成
student1.assignGrade(Course.EnglishList);
在编译
阶段是不能发现问题的,如果用
J2SE 5.0
枚举类型(
enum
)可以避免这些问题
.
- 枚举类型每一个值都是public, static and final的.也就是说,这些值是唯一的而且一旦定义了是不能被重写或修改.而且尽管在枚举类型每一个值声明时没有出现static关键字,实际上值都是静态的, 而且我们不能在值前面加上static, public,final 修饰符,否则就会出现下图 6的错误.
图
5
枚举类型值的错误声明
- 枚举类型都实现了java.lang.Comparable,枚举类型的值是可以比较排序的,排列顺序就是枚举类型定义这些值的顺序.
1.3
枚举类型的应用
下面各小节介绍了枚举类型的各种应用 .
下面各小节介绍了枚举类型的各种应用 .
XMLns:xsi="http://www.w3.org/2001/XMLSchema-instance">1.3.1
循环(
Iteration
)
当我们写程序时,常常遇到对数组或列表里的每一个对象进行处理的情况
.
在
J2SE 5.0
以前,如果要在一个数组或列表里进行轮循时,我们的做法比较繁琐,需要借助
Java.util.Iterator
类,
如下所示:
清单
5
:
|
现在我们可以通过
J2SE 5.0
的
for/in loop
和枚举类型一起使用
.
这能使以前花很多时间写的程序简单化,如上面清单
5
的程序可简化为:
清单
6
:
|
我们把上面的伪代码写成程序在
Eclipse3.1
上运行,如下图所示,在右下控制平台视图里显示了运行结果
.
如果看不见控制平台,点击
Window->Other Views->Console,
控制平台就会出现在右下角
.
图
6
枚举类型在循环中的应用
我们在使用
for/in loop
时要求它的表达式要求必须是数组或者是实现了
java.lang.Iterable
的集合,而枚举类型的
values()
函数返回的就是一个数组
.
另外循环变量的声明必须是在
loop
里
,
包括变量类型和变量名
.
我们不能在循环里使用一个在循环之外声明的变量
.
这和
J2SE 5.0
以前
for loop
里用的循环变量的声明不同
.
1.3.2
转换(
Switch
)
我们常用的一种判断语句就是
Switch-case
语句
.
在
Switch
语句中使用枚举类型,不仅能简化程序,而且增强了程序的可读性
.
清单
8.
|
在
Switch
语句里使用枚举类型时,一定不能在每一个枚举类型值的前面加上枚举类型的类名,否则编译器就会报错
(
会导致编译错误
).
我们把上面的程序稍作修改,在
case
语句里加上枚举类型的类名并运行在
Eclipse 3.1
平台上
.
我们发现
Eclipse
的问题视图里提示
case
语句里枚举类型值的前面加上枚举类型的类名是错误的,
如下图
8
所示
.
图
7: case
语句里枚举类型的值
原因是
J2SE 5.0
的实现要求
case
语句里每一个枚举类型值是不能有枚举类型类作为前缀的
.
前面谈到过每一个枚举类型的值都是枚举类型的一个实例
.
那么当编译器编译
case
语句时
,
是如何处理这些实例的
?
这有两种情况:如果
switch
与枚举类型定义在同一个编译单元
,
第一次编译时一个新表会创建在内存里
.
在这个表里
,
每一个枚举类型的值都和它在枚举类型里定义的顺序关联起来
.
编译器编译结果就和下面清单
9
显示的的程序很像
.
只不过顺序号没有加到程序里
,
而是编译器在表里快速查询
.
如果枚举类型被修改或从定义
,
表会被更新
.
清单
9:
|
还有一种经常出现的情况是
switch
与枚举类型定义不是在同一个编译单元
.
在这种情况下
,
大多数编译器就会把
switch-case
语句翻译成一系列的
if/else
语句
:
清单
10:
|
1.3.3
Maps of Enum and Sets of Enum
在
J2SE 5.0
的
java.util
程序包中提供两个新类:
EnumMap
和
EnumSet
,这两个类与枚举类型的结合应用可使以前非常繁琐的程序变得简单方便
.EnumMap
类提供了
java.util.Map
接口的一个特殊实现,该接口中的键(
key
)是一个枚举类型
.
清单
11:. EnumMap
例子
|
EnumSet
类提供了
java.util.Set
接口的实现,该接口保存了某种枚举类型的值的集合
.EnumSet
的作用类似于特性的集合,或者类似于某个枚举类型的所有元素的值的子集
.EnumSet
类拥有一系列的静态方法,可以用这些方法从枚举类型中获取单个元素或某些元素,
下面的程序例子显示如何这些静态方法:
清单
12:.EnumSet
例子
|
我们在
Eclipse3.1
环境中运行上面的程序,结果如下图:
图
8: EnumSet
样例运行结果
1.3.4
枚举类型的函数定义
在介绍创建枚举类型中曾提到枚举类型都是
java.lang.Enum
的子类
.
也就是说
,
枚举类型都是可编译的
java
的类,那么就可以在枚举类型里添加构造函数和其它函数,如清单
13
里的
getDescription
()
清单
13:
|
枚举类型的函数定义的应用是很有用的,
例如可以让多个枚举类型实现同一个
interface
来达到程序设计的模式化
.
例如一个定义了
getDescription
()接口的
interface,
让有同样需求的不同枚举类型来实现它
.
上面的
colorFeature
可以实现它
,
另一个
FontFeature
也可以实现它
.
1.3.5
特定于常量的类主体
在上一节里提到枚举类型可以定义自己的函数,其实更进一步,枚举类型的每一个值都可以实现枚举类型里定义的抽象函数,这听起来很不可思议,我们可以先看下面的例子
.
|
枚举类型
Priority
定义了一个抽象函数
perform()
,
Priority
的每一个值都对
perform
函数实现了重载,这就是枚举类型的特定于常量的类主体
.
在这种情况下,每声明一个值,枚举类型的一个子类生成,然后生成这个子类的唯一的实例来表示这个值
.
不同的值,就有对应的不同的子类
.
每个子类可以对父类的抽象函数进行重载
.
我们可以用下面的程序在
Eclipse3.1
环境中运行来证明此时
3
个子类生成
.
|
运行结果如下图
10.
图
9
测试特定于常量的类主体运行结果
由于特定于常量的类主体难理解容易出错,它的应用比较少,大多数情况下可以用
switch-case
语句替代
.
在这里简单介绍,仅供参考
.
1.4
枚举类型的小结
使用枚举类型是很简单的 . 它定义一个固定的、封闭的值集合,然后,在需要这些值中的某一个值时,可以通过它的名称来指定它,这就是枚举类型的简单性 . 枚举类型的值就是枚举类型的实例,编译器会确保没有传入其他的类型,这就是枚举类型的安全性 . 这些枚举类型就是类本身,因此,可以对类进行的所有操作同样可以作用于枚举类型上 . 我们要小心使用构造函数和函数重载方法,不要因为这些特性可用就任意使用它们 . 比如特定于常量的类主体,大多情况下可以用 Switch 语句来代替,更容易让人理解而且不容易出错 . 我们也看到了 Eclipse 3.1 平台对枚举类型的支持,包括提供创建模板,错误信息提示等 . 总之,枚举类型的灵活应用能极大的方便和简化了我们的开发工作。
使用枚举类型是很简单的 . 它定义一个固定的、封闭的值集合,然后,在需要这些值中的某一个值时,可以通过它的名称来指定它,这就是枚举类型的简单性 . 枚举类型的值就是枚举类型的实例,编译器会确保没有传入其他的类型,这就是枚举类型的安全性 . 这些枚举类型就是类本身,因此,可以对类进行的所有操作同样可以作用于枚举类型上 . 我们要小心使用构造函数和函数重载方法,不要因为这些特性可用就任意使用它们 . 比如特定于常量的类主体,大多情况下可以用 Switch 语句来代替,更容易让人理解而且不容易出错 . 我们也看到了 Eclipse 3.1 平台对枚举类型的支持,包括提供创建模板,错误信息提示等 . 总之,枚举类型的灵活应用能极大的方便和简化了我们的开发工作。