Java枚举类型

在Java中,枚举类型使用enum关键字来定义。枚举类型是一种特殊的数据类型,用于定义和使用一组常数。如果把enum类型当做一种特殊的类(class),那么它的实例必须是预先定义好的常数当中的一个。当我们要使用一组固定的常数时,使用枚举类型(当然,在接口当中定义静态常量也是可以的),例如:

 

  1. <span style=”font-family:Microsoft YaHei;font-size:12px;”>/**
  2.  * @author Brandon B. Lin
  3.  *
  4.  */
  5. public enum Direction {
  6.      NORTH, SOUTH, EAST, WEST
  7. }
  8. </span>

上面的代码定义了一个枚举类型,有四个常量。每个枚举常量都是枚举类型的实例对象,被隐式地声明为该枚举类型的public、static、final成员。有了枚举类型之后,我们就可以创建枚举类型的变量(对比数组类型的变量)。下面的代码简单地使用了这个枚举类型:

 

 

  1. <span style=”font-family:Microsoft YaHei;font-size:12px;”>/**
  2.  * @author Brandon B. Lin
  3.  *
  4.  */
  5. public class TestEnum {
  6.     /**
  7.      * @param args
  8.      */
  9.     public static void main(String[] args) {
  10.         Direction direction = Direction.EAST; //枚举类型变量
  11.         switch (direction) {
  12.         case EAST:
  13.             System.out.println(“east”);
  14.             break;
  15.         case NORTH:
  16.             System.out.println(“north”);
  17.             break;
  18.         default:
  19.             break;
  20.         }
  21.     }
  22. }</span>

注意到在direction = Direction.EAST这个语句当中,使用EAST这个枚举类型实例的时候,格式为Direction.EAST,也就是EAST是DIrection枚举类型的实例,但同时它又以public static final成员的身份存在。但是在case语句中,我们没有使用枚举类型名,而是直接使用EAST或者NORTH,之所以可以这么做,是因为switch(direction)已经隐式地指定了case常量的枚举类型,也就是通过direction的类型来判断case后面常量的类型。一点题外话,swith除了可以使用byte、char、short、int类型外,还可以使用String类型和枚举类型。

 

理解枚举类型(enum type)一种比较直观的方式就是把它当做一种特殊的class,特殊之处在于它的实例必须是常数组中的某个值。既然把枚举类型当做一种特殊的类,那么它应该也可以定义域和方法,就像一般的类一样,答案是肯定的。在Java的enum类型中,可以定自己的域和方法。虚拟机创建一个枚举类型的时候,自动加入了一些常用的方法,虽然这些方法没有在java.lang.Enum的API列出来(下面会说的所以enum类型都继承自Enum)。比如,有一个静态的方法values,它返回一个数组,其中数组的元素为定义枚举类型的时候指定的常数。这一方法常常和for-each结构一起使用,例如在上面的例子中,我们加入以下代码:

 

  1. <span style=”font-family:Microsoft YaHei;font-size:12px;”>for(Direction d:Direction.values()) {
  2.             System.out.println(d);
  3.         }</span>

那么就会依次输出NORTH、SOUTH、EAST、WEST。枚举类型常量在println方法中,输出的是预先定义的常量的名称。

 

 

一点说明,所有的枚举类型都隐式地继承java.lang.Enum,虽然我们没有extends,又因为java使用单继承机制,所有enum类型不能再继承其他类,也不能被继承。所以使用Eclipse创建枚举类型的时候,你会发现你不能在初始的界面中添加父类,但是接口仍然可以,下面是创建枚举类型和创建class的界面对比:

 

除了预定义的values方法以外,因为继承自Enum,所以也继承了一些有用的方法,简单介绍如下:

 

  1. <span style=”font-family:Microsoft YaHei;font-size:12px;”>public final String name()</span>

这个方法以字符串返回枚举类型实例(事先定义好的)的名字,也就是在定义枚举类型时给出的常数(如上面的EAST、NORTH)。例如,Direction.EAST.name()将返回“EAST”。

 

 

  1. <span style=”font-family:Microsoft YaHei;font-size:12px;”>public final int ordinal()</span>

这个方法返回枚举类型实例在定义时候的索引为止(从0开始),例如,Direction.EAST.ordinal返回2.说明定义时候它排在第3位。

 

public static enum-type valueOf(String str)

这个方法返回枚举常量名为str的枚举类型实例(常量),例如Direction.valueOf(“EAST”)将返回EAST常量(注意EAST常量是枚举类型的一个实例)

final int compareTo(enum-type e)

调用这个方法的枚举常量类型必须和方法参数中的enum-type相同,否则无法比较。需要指出的是,这里比较的是枚举常量的ordinal(原始值),也就是位置索引。调用方法的常用在方法参数的前面返回负值,类似的,相等的时候返回0,在后面返回正值。

如果想要判断两个枚举常量是的相等性,可以使用equals方法,虽然equals可以将枚举类型与其他对象(不是该枚举类型的常量)进行比较,但是总是返回false,没有意义。只有当比较的两者属于同一枚举类型并且是相同的枚举常量,才返回true。当然,也可以用“==”来比较两个枚举类型常量。

我们可以为枚举类型提供构造函数、添加实例变量和方法,甚至可以实现接口。为了进一步说明枚举类型的域和方法,我们使用Java tutorial中的例子来说明。先上代码,再做说明:

 

  1. <span style=”font-family:Microsoft YaHei;font-size:12px;”>public enum Planet {
  2.     MERCURY (3.303e+23, 2.4397e6),
  3.     VENUS   (4.869e+24, 6.0518e6),
  4.     EARTH   (5.976e+24, 6.37814e6),
  5.     MARS    (6.421e+23, 3.3972e6),
  6.     JUPITER (1.9e+27,   7.1492e7),
  7.     SATURN  (5.688e+26, 6.0268e7),
  8.     URANUS  (8.686e+25, 2.5559e7),
  9.     NEPTUNE (1.024e+26, 2.4746e7);
  10.     private final double mass;   // in kilograms
  11.     private final double radius; // in meters
  12.     Planet(double mass, double radius) {
  13.         this.mass = mass;
  14.         this.radius = radius;
  15.     }
  16.     private double mass() { return mass; }
  17.     private double radius() { return radius; }
  18.     // universal gravitational constant  (m3 kg-1 s-2)
  19.     public static final double G = 6.67300E-11;
  20.     double surfaceGravity() {
  21.         return G * mass / (radius * radius);
  22.     }
  23.     double surfaceWeight(double otherMass) {
  24.         return otherMass * surfaceGravity();
  25.     }
  26.     public static void main(String[] args) {
  27.         if (args.length != 1) {
  28.             System.err.println(“Usage: java Planet <earth_weight>”);
  29.             System.exit(-1);
  30.         }
  31.         double earthWeight = Double.parseDouble(args[0]);
  32.         double mass = earthWeight/EARTH.surfaceGravity();
  33.         for (Planet p : Planet.values())
  34.            System.out.printf(“Your weight on %s is %f%n”,
  35.                              p, p.surfaceWeight(mass));
  36.     }
  37. }
  38. </span>

首先是常量的定义,Java要求常量的定义必须首先出现,在域和方法之前。如果有域和方法,那么在常量定义全部结束的时候,必须使用分号(注意到如果没有域和方法时,分号可以省略)。在这个例子中,定义了几个常量,注意到定义常量的时候同时传递了参数,当常数被创建的时候,构造函数被调用一次,同时这些参数传递给构造函数。域和方法的定义和普通类完全相同,注意的是,所有的枚举类型实例都是实现定义好的,实例必须是这其中的一个,所以调用方法的时候也必须用这些常量来调用,如上面的EARTH.surfaceGravity().

 

枚举类型的构造器也可以被重载(overload),例如有如下两个构造器

 

 

枚举类型一般定义为top-level class,但是也可以定义为嵌套类,即在类内部定义枚举类型,此时,枚举类型自动变成static(即使没有显式声明),所以,枚举类型的实例(事先定义好的)不能访问外部类的实例成员(实例域和实例方法)。

 

  1. public Apple() {…}
  2. public Apple(int price) {…}

因此我们在定义枚举常量的时候,可以选择合适的构造器来创建常量,如:

 

 

  1. enum Apple {
  2.     AA(10), BB(8), CC,DD(1);
  3. }

 

最后总结一下:

枚举类型是一种特殊的类,它可能的实例是事先定义好的,每个实例都有它自己的状态,并且这些实例自动被创建,以静态域(public static final)的方式保存。静态域的名字就是我们定义常量组时候设置的名字。十分重要的是,每个枚举常量都是定义它的类的对象!!在设计模式中,枚举类型是一种Flyweight模式,也就是类的实例只能是有限个事先定义好的其中一个。



发表评论

电子邮件地址不会被公开。 必填项已用 * 标注

*

您可以使用这些 HTML 标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

(Spamcheck Enabled)