枚举

   JDK5之前的Java版本缺失一项特性是枚举。形式最简单地枚举(enumeration)是一些列具有名称的常量。尽管Java提供了其他一些能提供类似功能的特性,例如final变量,但是许多程序员仍然怀念枚举概念的单纯性——特别是因为大多数其他常用语言都支持枚举。从JDK5开始,枚举被添加到了Java语言中。
   形式最简单的枚举,看起来和其他语言中的枚举类似。但是,这种相似性只是表面上的。因为在Java中,枚举定义了一种类类型。通过将枚举定义为类,极大地扩展了枚举的功能。例如在Java中,枚举可以具有构造函数、方法以及实例变量。

1、 枚举的基础知识

   创建枚举需要使用关键字enum。例如,下面是一个简单的枚举,其中列出了各种苹果的品种:

//An enumeration of apple varieties.
enum Apple{
Jonathan,GoldenDel,RedDel,Winesap,Cortland
}

   标识符Jonathan、GoldenDel等被称为枚举常量。每个枚举常量被隐式声明为Apple的公有、静态final成员。此外,枚举常量的类型是声明它们的枚举的类型,对于这个例子为Apple。因此在Java语言中,这些常量被称为是“自类型化的”(self-typed),其中的“自”是指封装常量的枚举。
   定义了枚举之后,可以创建枚举类型的变量。但是,尽管枚举定义了类类型,却不能使用new实例化枚举。反而,枚举变量的声明和使用方式在许多方面与基本类型相同。例如,下面这行代码将ap声明为Apple枚举类型的变量:

Apple ap;

   因为ap是Apple类型,所以只能被赋值为(或包含)在Apple枚举中定义的那些值。例如,下面这行代码将ap赋值为RedDel:

ap = Apple.RedDel;

   注意在符号RedDel之前的Apple。
   可以使用关系运算符“==”比较两个枚举常量的相等性。例如,下面这条语句比较ap的值和GoldenDel常量:

if(ap == Apple.GoldenDel)

   枚举值也可以用于控制switch语句。当然,所有case语句使用的常量的枚举类型,都必须与switch表达式使用的枚举类型相同。例如,下面这条switch语句是完全合法的:

//Use an enum to control a switch statement.
switch(ap){
  case Jonathan:
    // ...
  case Winesap:
   // ...
}

   注意在case语句中,枚举常量的名称并没有使用枚举类型的名称进行限定。也就是说,使用的是Winesap而不是Apple.Winesap。这是因为switch表达式中的枚举类型已经隐式指定了case常量的枚举类型,所以在case语句中不需要使用枚举类型的名称对常量进行限定。实际上,如果视图这么做的话,会造成编辑时错误。
  &nbsp当显示枚举常量时,例如在println()语句中,会输出枚举常量的名称。例如下面这条语句:

System.out.println(Apple.Winesap);//显示名称“Winesap”

   下面的程序用到了刚才介绍的所有内容,并演示了Apple枚举:

//An enumeration of apple varieties
public enum Apple {
    Jonathan,GoldenDel,RedDel,Winesap,Cortland
}
public class EnumDemo {
    public static void main(String[] args) {
        Apple ap;
        ap = Apple.RedDel;
        //Output an enum value.
        System.out.println("Value of ap: " + ap);
        System.out.println();
        ap = Apple.GoldenDel;
        //Compare two enum value.
        if (ap == Apple.GoldenDel)
            System.out.println("ap contains GoldenDel.\n");
        //Use an enum to control a switch statement.
        switch (ap) {
            case Jonathan:
                System.out.println("Jonathan is red.");
                break;
            case GoldenDel:
                System.out.println("Golden Delicious is yellow.");
                break;
            case RedDel:
                System.out.println("Red Delicious is red.");
                break;
            case Winesap:
                System.out.println("Winesap is red.");
                break;
            case Cortland:
                System.out.println("Cortland is red.");
                break;
        }
        /**
         * 输出结果:
         * Value of ap: RedDel
         * ap contains GoldenDel.
         * Golden Delicious is yellow.
         */
    }
}
2、values()和valueOf()方法

   所有枚举都自动包含两个预定义方法:values()和valueOf()。它们的一般形式如下所示:

public static enum_type [] values()
public static enum_type valueOf(String str)

   values()方法返回一个包含枚举常量列表的数组,valueOf()方法返回与传递到参数str的字符串相对应的枚举常量。例如,对于前面显示的Apple枚举,Apple.valueOf(“Winesap”)的返回类型是Winesap。
   下面的程序演示了values()和valueOf()方法:

public class EnumDemo2 {
    public static void main(String[] args) {
        Apple ap;
        System.out.println("Here are all Apple constants:");
        //use values();
        for (Apple a : Apple.values()) {
            System.out.println(a);
        }
        System.out.println();
        //use valueOf
        ap = Apple.valueOf("Winesap");
        System.out.println("ap contains " + ap);
    }
    /**
     * 输出结果:
     * Here are all Apple constants:
     * Jonathan
     * GoldenDel
     * RedDel
     * Winesap
     * Cortland
     *
     * ap contains Winesap
     */
}
3、枚举是类类型

   正如前面所解释的,Java枚举是类类型。虽然不能使用new实例化枚举,但是枚举却有许多和其他类相同的功能。枚举定义了类,这为Java枚举提供了超乎寻常的功能。例如,可以为枚举提供构造函数、添加实例变量和方法,甚至可以实现接口。
   需要理解的重要一点是:每个枚举常量都是岁数枚举类型的对象。因此,如果为枚举定义了构造函数,那么当创建每个枚举常量时都会调用该构造函数。此外,对于枚举定义的实例变量,每个枚举常量都有它自己的副本。例如,分析下面版本的Apple枚举:

//Use an enum of constructor,instance variable,and method.
public enum Apple {
    Jonathan(10), GoldenDel(9), RedDel(12), Winesap(15), Cortland(8);
    private int price;//price of each apple

    //Constructor
    Apple(int p) {
        price = p;
    }

    int getPrice() {
        return price;
    }
    }
public class EnumDemo3 {
    public static void main(String[] args) {
        Apple ap;
        //Display price of Winesap.
        System.out.println("Winesap costs " + Apple.Winesap.getPrice() + " cents.\n");
        //Display all apples and price
        System.out.println("All apple prices:");
        for (Apple a : Apple.values()) {
            System.out.println(a + " consts " + a.getPrice() + " cents.");
        }
        /**
         * 输出结果:
         * Winesap costs 15 cents.
         *
         * All apple prices:
         * Jonathan consts 10 cents.
         * GoldenDel consts 9 cents.
         * RedDel consts 12 cents.
         * Winesap consts 15 cents.
         * Cortland consts 8 cents.
         */
    }
}

   这个版本的Apple枚举添加了3个内容:第1个内容是实例变量price,用于保存每种苹果的价格;第2个内容是Apple构造函数,它以苹果的价格作为参数;第3个内容是getPrice()方法,用于返回price变量的值。
   当在mian()方法中声明变量ap时,对于每个特定的常量调用Apple构造函数一次。注意指定构造函数参数的方式,通过将他们放置到每个常量后面的圆括号中来加以指定,如下所示:

Jonathan(10), GoldenDel(9), RedDel(12), Winesap(15), Cortland(8);

   这些数值被传递给Apple()的参数p,然后将这些值赋给price变量。再强调一次,为每个常量调用构造函数一次。因为每个枚举常量都有自己的price变量副本,所以可以调用getPrice()方法来获取指定类型苹果的价格。例如在main()方法中,通过下面的调用获取Winesap的价格:

Apple.Winesap.getPrice()

   通过for循环遍历枚举可以获取所有品种的苹果的价格。因为每个枚举常量都有price变量的副本,所以与枚举常量关联的值是独立的,并且和其他常量关联的值不同。这是一个强大的功能,只有将枚举作为类实现,像Java这样,才会具有这种功能。
   枚举可以提供两种甚至更多种重载形式,就像其他类那样。例如,下面版本的Apple提供了一个默认构造函数,可以将price变量初始化为-1,表明不能获得价格数据:

//Use an enum of constructor,instance variable,and method.
public enum Apple {
    Jonathan(10), GoldenDel(9), RedDel, Winesap(15), Cortland(8);
    private int price;//price of each apple

    //Constructor
    Apple(int p) {
        price = p;
    }

    //Overloaded constructor
    Apple() {
        price = -1;
    }

    int getPrice() {
        return price;
    }
}

   注意在这个版本中,没有为RedDel提供参数。这意味着会调用默认构造函数,并将RedDel的price变量设置为-1。
   枚举有两条限制:第一,枚举不能继承其他类;第二,枚举不能是超类。这意味着枚举不能扩展。在其他方面,枚举和其他类很相似。需要记住的关键是:每个枚举常量都是定义它的类的对象。

4、枚举继承自 Enum 类

   尽管声明枚举时不能继承超类,但是所有枚举都自动继承超类java.lang.Enum,这个类定义了所有枚举都可以使用的一些方法。
   可以获取用于指示枚举常量在常量列表中位置的值,这称为枚举常量的序数值。通过ordinal()方法可以检索序数值,该方法的声明如下所示:

final int ordinal()

   该方法返回调用常量的序数值,序数值从0开始。因此在Apple枚举中,Jonathan的序数值为0,GoldenDel的序数值为1,等等。
   可以使用compareTo()方法比较相同类型的两个枚举常量的序数值,该方法的一般形式如下:

final int compareTo(enum-type e)

   其中enum-type是枚举的类型,e是和调用常量进行比较的常量。请记住,调用常量和e必须是相同的枚举。如果调用常量的序数值小于e的序数值,那么compareTo()方法返回负值;如果两个序数值相同,就返回0;如果调用常量的序数值大于e的序数值,就返回正值。
   可以使用equals()方法来比较枚举常量和其他对象的相等值,该方法重写了Object类定义的equals()方法。尽管equals()方法可以将枚举常量和任意其他对象进行比较,但是只有当两个对象都引用同一枚举中相同的常量时,它们才相等。如果两个常量来自不同的枚举,那么即使它们的序数值相同,equals()方法也不会返回true。
   请记住,可以使用"=="比较两个枚举引用的相等性。
   下面演示了ordinal()、compareTo()以及equals()方法:

public class EnumDemo4 {
    public static void main(String[] args) {
        Apple ap, ap2, ap3;
        //Obtain all ordinal values using ordinal().
        System.out.println("Here are all apple constants and their ordinal values: ");
        for (Apple a : Apple.values())
            System.out.println(a + " " + a.ordinal());
        ap = Apple.RedDel;
        ap2 = Apple.GoldenDel;
        ap3 = Apple.RedDel;
        System.out.println();
        if (ap.compareTo(ap2) < 0)
            System.out.println(ap + " comes before " + ap2);
        if (ap.compareTo(ap2) > 0)
            System.out.println(ap2 + "comes before " + ap);
        if (ap.compareTo(ap3) == 0)
            System.out.println(ap + " equals " + ap3);
        System.out.println();
        if (ap.equals(ap2))
            System.out.println("Error!");
        if (ap.equals(ap3))
            System.out.println(ap + " equals " + ap3);
        if (ap == ap3)
            System.out.println(ap + " == " + ap3);
        /**
         * 输出结果
         * Here are all apple constants and their ordinal values:
         * Jonathan 0
         * GoldenDel 1
         * RedDel 2
         * Winesap 3
         * Cortland 4
         *
         * GoldenDelcomes before RedDel
         * RedDel equals RedDel
         *
         * RedDel equals RedDel
         * RedDel == RedDel
         */
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值