首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 开源 FAQ 第二书店 博文视点 程序员
频道: 研发 数据库 中间件 信息化 视频 .NET Java 游戏 移动 服务: 人才 外包 培训
    图书品种:235680
       
热门搜索: ASP.NET Ajax Spring Hibernate Java

1.2  计数:使用枚举

大多数应用程序需要记录一个值的有限集—— 即应用程序中表示一组选择或状态的常量。一种常见的Java编程惯例是使用static int变量来表示这些值。然后让程序通过比较这些值和其他变量的值来做出决定。尽管核心Java API本身也采用这种惯例,但它很可能导致严重的问题!下面是一个有关水果信息的示例。该示例给出了使用int变量来表示枚举数据时会出现的一些问题。

public class FruitConstants {

// this is not such a good practice

public static final int APPLE = 1;

public static final int ORANGE = 2;

public static final int GRAPEFRUIT = 3;

public static final int BANANA = 4;

public static final int DURIAN = 5;

public static final int CITRUS = 6;

public static final int SWEET = 7;

public static final int SMELLY = 8;

public static final int UNKNOWN = 9;

public static int getCategory(int fruit) {

switch(fruit) {

case APPLE: case BANANA:

return SWEET;

case ORANGE: case GRAPEFRUIT:

return CITRUS;

case DURIAN:

return SMELLY;

}

return UNKNOWN;

}

}

其中,来自东南亚的水果榴莲(Durian)具备“Sweet”和“Smelly”,但是这里的水果只能返回一个特点,也就是说,所有的值只能是对应单一的值而不是对应多个值。另外一个主要的问题是任何int值都可以传递给getCategory方法,无论它是否代表一个有效的水果。这可能导致细微的错误,因为编译器不关心是调用getCategory(SWEET)还是调用getCategory(42)。并且如果整型常量的值发生了变化,getCategory(3)显示的将不再是正确的信息!

另一个问题是使用水果和类别的int值并无区别—— 它们都只是普通的int值。通过简单地将类别常量置于一个不同的类中可以部分地解决水果和类别分离的问题,但是它们仍只是int值而不是类型安全(typesafe)的,也就是没能将getCategory的参数限制到一个固定的值集合。

在Java 5中,有一个优雅的解决方法:可以像在C语言中那样创建枚举类型。这是一个新特性,它创建一个类,该类包含一个所有它允许的实例清单。除了enum内定义的实例外,不允许其他的实例。下面看一些enum的示例:

enum Fruit {APPLE, ORANGE, GRAPEFRUIT, BANANA, DURIAN}

enum FruitCategory {SWEET, CITRUS, SMELLY, UNKNOWN}

enum Dessert {PIE, CAKE, ICECREAM, BROWNIE}

以上每个例子均定义了一组不同的枚举元素(选择)。这样做的好处是不会将Fruit值与其他类型的相混合或相混淆。对待每个enum就好像它是一个不同的类一样。不能将FruitCategory作为一个参数传递给一个期望Dessert的值方法,也不能传递一个int值。下面扩展Fruit enum以包含最初的FruitConstants类具有的功能:

public enum Fruit {

APPLE, ORANGE, GRAPEFRUIT, BANANA, DURIAN;

public static FruitCategory getCategory(Fruit fruit) {

switch(fruit) {

case APPLE: case BANANA:

return FruitCategory.SWEET;

case ORANGE: case GRAPEFRUIT:

return FruitCategory.CITRUS;

case DURIAN:

return FruitCategory.SMELLY;

}

return FruitCategory.UNKNOWN;

}

}

可以看出一个enmu也可以像一个类那样定义方法。现在的getCategory方法采用Fruit作为参数,并且只允许使用enum中定义的值。接下来这个代码段将引起编译错误,而不会出现调用最初的未受保护的getCategory方法时出现的运行时异常:

Fruit.getCategory(Dessert.PIE); // compile error

Fruit.getCategory(10); // compile error

如果每种水果管理它自己的类别将会更好,因此为了完善Fruit类,将删除getCategory的水果参数并使该方法为每个enum状态返回不同的值。通过创建一个适用于所有值的抽象的getCategory方法并对每个enum以不同的方式重写它,可以完成此操作。它非常类似于为每个枚举值编写一个不同的子类,并让每个这样的子类重写抽象的方法。

public enum Fruit {

APPLE

{ FruitCategory getCategory() {return FruitCategory.SWEET;} },

ORANGE

{ FruitCategory getCategory() {return FruitCategory.CITRUS;} },

GRAPEFRUIT

{ FruitCategory getCategory() {return FruitCategory.CITRUS;} },

BANANA

{ FruitCategory getCategory() {return FruitCategory.SWEET;} },

DURIAN

{ FruitCategory getCategory() {return FruitCategory.SMELLY;} };

abstract FruitCategory getCategory();

}

一旦创建一个类似这样的enum,就可以像对待其他对象那样来对待APPLE值(使用静态的Fruit.APPLE引用),并且可以调用它的getCategory方法来得到它的相应类别。现在可以为上面的类添加一个main方法来说明如何使用新的Fruit enum:

public static void main(String[] args) {

Fruit a = Fruit.APPLE;

// toString() returns "APPLE"

System.out.println ("The toString() for a: " + a);

// getCategory() returns "SWEET"

System.out.println ("a.getCategory() is: " + a.getCategory());

for (Fruit f : Fruit.values()) {

System.out.println ("Fruit is: " + f);

}

}

正如代码中所示的那样,可以使用values方法来迭代enum内的所有值。enum的toString方法将会返回一个String,它具有和值相同的名字。使用enum而不是int来表示状态,可以使代码具有更好的可读性和更强的防错性。它明确地定义了一个特殊的枚举状态的所有值并可以防止有人使用不正确的值。

查看所有评论(0)条】

最近评论



正在载入评论列表...
热点评论