Java暗箱操作之enum

  enum,即枚举类型,在每种编程语言中都有类似的类型。

  因为用得少,我每次看到enum都会望而生畏,因为它的语法实在是“不按常规出牌”啊!

  

  一般的enum语法是这样的:

public class MyClass {
    private enum Fruit {APPLE, ORANGE, GRAPE, BANANA}  //类型定义

    private Fruit fruit = Fruit.APPLE;    //类型使用

    private void useEnum() {
        if (fruit == Fruit.ORANGE) {
              System.out.println("这是橘子");
       }
    }
}

  在上面的“类型定义”部分,就发现定义一个enum类型的语句跟常规的Java语法有很大出入,一个类里面一般就是定义变量、函数、内部类这三样东西,但是这句话感觉什么都不是。

  对于enum,包括C++当中的类似的枚举类型,我以前都是采取死记硬背的方法,把它跟其他的语法区别对待来记忆。然而并没有卵用,就是因为枚举类型的“不寻常”,以及用得少,很快就会忘记具体的语法格式。

  前两天看《Effective Java》时,发现有一章专门讲enum的,而且书中强调用enum的好处,以及Java当中的enum相比其他语言的枚举类型的强大之处。我看得一知半解,但对enum这一特殊的东西开始重视起来。

  通过各方面的研究,我发现了enum不为人知的惊天内幕,以及各种暗箱操作。

  首先给出一个最重要的点:enum其实是一个class!

  有了这一逻辑,对于enum的基础语法就理解了一半。把上面的“enum”这个词换成“class”试试会怎样?比如枚举值两边用大括号是怎么回事,是不是清楚了很多?

  

  既然enum就是一个类,那我把 Fruit类(可以这么叫了吧?)的定义换个形式

public class MyClass {
    private class Fruit {      //把enum换成新的定义
        private Fruit() {}      //不允许在外面new出Fruit
        public static final Fruit APPLE = new Fruit();
        public static final Fruit ORANGE = new Fruit();
        public static final Fruit GRAPE = new Fruit();
        public static final Fruit BANANA = new Fruit();
    }    

    private Fruit fruit = Fruit.APPLE;    //类型使用

    private void useEnum() {
        if (fruit == Fruit.ORANGE) {
              System.out.println("这是橘子");
       }
    }
}

  看完这段转换之后的用常规class写的代码,是不是想惊呼一声:这特么不是效果完全一样的代码吗!

  是的,这就是编译器的暗箱操作,编译器一看到enum这个关键字,就会很自然地试图转换成这样。每一个枚举值,并不是C++里面那样的从0开始的int值,而是一个个类实例。而且因为加上了final关键字,知道为什么枚举值是全大写的了吧?又因为加了static关键字,为什么会写出“Fruit.APPLE”这样的静态调用形式了吧?

  这还没有完,以上只是enum定义时的原理,enum在具体使用时,如何遍历?如何输出每个枚举值代表的int值?如何输出每个枚举值的字面字符串值?

  事实上,上面给出的Fruit类很不完整,替代enum的类继承自java.lang.Enum,这是一个抽象类,编译器遇到enum时自动转化为Enum父类中对应的操作。

  

//Enum类的定义
public abstract class Enum<E extends Enum<E>> implements Serializable, Comparable<E> {...}

比如上面的Fruit类其实是这样的

private class Fruit extends java.lang.Enum {
      ....
}

  来看一下Enum中主要的成员。

//这是Enum类仅有的两个成员变量,比如{APPLE,ORANGE,GRAPE},3个枚举值其实都是Enum类的子类的实例,name分别是"APPLE","ORANGE","GRAPE", ordinal分别是0,1,2

private final String name;    //枚举值的字面字符串表示,比如"APPLE","ORANGE",

private final int ordinal;      //枚举值所代表的int值,跟C++中的枚举类型类似,可以理解为索引,按照定义时顺序,第一个为0,第二个为1,以此类推

  可以通过类似于get方法得到这两个属性值

    //得到字面值
    public final String name() {
        return name;
    }

    //得到索引值
    public final int ordinal() {
        return ordinal;
    }

  另外在输出枚举值时,输出的是枚举值的字面字符串值,因为调用的是toString()方法

    @Override
    public String toString() {
        return name;
    }

  Enum类实现了Comparable接口,因而可以进行枚举值间的比较,事实上就是索引值比较

    //返回两个枚举值的索引值之差
    public final int compareTo(E o) {
        return ordinal - ((Enum<?>) o).ordinal;
    }

  enum还有两个操作应该是编译器调用Enum类的其他方法实现的。

  第一个是enum的values()方法,返回所有的类实例组成的数组,比如  enum Fruit {APPLE,ORANGE,GRAPE},就返回 new Fruit[]{Fruit.APPLE, Fruit.ORANGE, Fruit.GRAPE},这可以用于遍历操作。

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

  第二个是用switch做选择的时候,case后面只要写出枚举值即可,不必写类名

Fruit f = Fruit.APPLE;
switch(f) {
case (APPLE) {  //此处不必也不能写成case (Fruit.APPLE),编译器自动判断这是Fruit类的实例
    ...
    break;
}
case (ORANGE) {
   ...
   break;
}
default {
   ...
}
}

  关于这是如何实现的,就是编译器自己的暗箱操作了,此处也不太清楚。

  另外,更高级一点的,就是把enum完全看成是一个class,因而可以重写自己的方法,添加自己的成员变量。例如用枚举值实现四则运算:

    public enum Operation {
          //每个枚举值,即Operation实例,其实自己在声明时定义了一个匿名的类,继承了Operation类,匿名子类里面重写了父类Operation的apply方法
          PLUS {double apply(double x,double y) {return x+y;}},
          MINUS {double apply(double x,double y) {return x-y;}},
          TIMES {double apply(double x,double y) {return x*y;}},
          DIVIDE {double apply(double x,double y) {return x/y;}};

          abstract double apply(double x, double y);    //Operation类中定义的抽象方法
    }

  上面的代码翻译过来其实是

    public class Operation extends Enum{

          public static final Operation PLUS = new Operation(){   //匿名内部类
                 @Override
                 double apply(double x,double y) {return x+y;}
          };
          ......

          abstract double apply(double x, double y);    //Operation类中定义的抽象方法
    }    

  当然也可以自己实现enum,添加成员变量,重载构造函数

public enum Operation {

     PLUS("+"),     //等于:public static final Operation PLUS = new Operation("+");
     MINUS("-"),
     TIMES("*"),
     DIVIDE("/");   

     private final String symbol;   //自己加的成员变量

     Operation(String symbol) {this.symbol = symbol;}  //构造函数,在列枚举值的时候可以加括号给出参数,注意访问权限最好别写成public 

     @Override
     public String toString() {return symbol;}   //重写Enum类的toString()方法
}

  最后,有一个小小的细节要注意,枚举值之间用“,”隔开。当enum定义中只有枚举值,没有其他东西时,枚举值最后可以不加“;”。但还有其他定义,比如还有一个成员方法,必须先写出枚举值,再加其他定义,且枚举值最后加“;”

    private enum Haha {

        private void f() {
            System.out.println("!!!!!!!!!");
        };
        FACE,SUGAR,APPLE;  //枚举值必须先写
    }    

枚举值的一大用处是用来替换零散的常量定义(public static final XXX XX = XX;),而且这些常量仅做标识符用,对于常量值是多少并不关心。使用了enum定义之后,可以进行类型检查,当常量很多时还可以归类,而且可以输出标识符的字面值。

时间: 2024-08-07 17:39:41

Java暗箱操作之enum的相关文章

深入掌握Java中的enum

对于要在程序中要表示有限种类的某事物,一般我们可以采用两种方式,一是使用:public static final String 常量:二是使用enum来表示.一般而言前者简单,但是不能够很好的提供更多的信息,而Java中的enum相比而言,却十分的强大,而且更加的专业. 1. 最间C风格的enum: /** * 数据源的类别:master/slave */ public enum DataSources { MASTER0, MASTER1, SLAVE0, SLAVE1, SLAVE2, SL

Java中的Enum的使用与分析(转)

示例: public enum EnumTest { FRANK("The given name of me"), LIU("The family name of me");//两个实例 private String context; private String getContext(){ return this.context; } private EnumTest(String context){ this.context = context; } publi

java 枚举(enum)学习

之前没有用过枚举,不懂.今天找了些资料学习了,现在总结如下:(希望高手看到后批评指教) 枚举的作用: 1.限定某一类型的取值范围. 2.不再写public static final...(如果取值范围太广,就太麻烦了),但最终enum还是要转化成class类型,还是会加public static final... 一段代码说明为什么java要有enum类型: package good.good.study; public class EnumStudy2 { public static void

Java枚举类型enum

枚举的语法 1.Enum的全称为enumeration,中文俗称枚举类,学过C/C++等语言的人,应该都对它略知一二. 但在Java语言规范中,是在JDK 5版本中才引入的,存放在 java.lang 包中.在Java版的Enum实质是语法糖,其声明方式如下: [代码1] package com.enumtest; public enum Color { RED,BLUE,BLACK,YELLOW,GREEN //注意这里可以没有分号 } enum是用来声明枚举的关键字,声明定义的类都隐含继承了

C++和Java中枚举enum的用法

在C++和java中都有枚举enum这个关键字,但是它们之间又不太一样.对于C++来说,枚举是一系列命名了的整型常量,而且从枚举值转化为对应的整型值是在内部进行的.而对于Java来说,枚举更像一个类的命名的实例,你可以自定义枚举的成员,枚举值转化为对应的整型值是再外部进行的.下面以我之前的一篇博客8.1 Implement Blackjack 实现21点纸牌来说明,里面有一个扑克牌花色的枚举类: // C++ defination enum Suit {Club, Diamond, Heart,

关于Java中枚举Enum的深入剖析

在编程语言中我们,都会接触到枚举类型,通常我们进行有穷的列举来实现一些限定.Java也不例外.Java中的枚举类型为Enum,本文将对枚举进行一些比较深入的剖析. 什么是Enum Enum是自Java 5 引入的特性,用来方便Java开发者实现枚举应用.一个简单的Enum使用如下. // ColorEnum.javapublic enum ColorEmun { RED, GREEN, YELLOW} public void setColorEnum(ColorEmun colorEnum) {

【转】掌握java枚举类型(enum type)

原文网址:http://iaiai.iteye.com/blog/1843553 1   背景 在java语言中还没有引入枚举类型之前,表示枚举类型的常用模式是声明一组具有int常量.之前我们通常利用public final static 方法定义的代码如下,分别用1 表示春天,2表示夏天,3表示秋天,4表示冬天. Java代码   public class Season { public static final int SPRING = 1; public static final int 

【转】java枚举类型enum的使用

原文网址:http://blog.csdn.net/wgw335363240/article/details/6359614 java 枚举类型enum 的使用 最近跟同事讨论问题的时候,突然同事提到我们为什么java 中定义的常量值不采用enmu 枚举类型,而采用public final static 类型来定义呢?以前我们都是采用这种方式定义的,很少采用enum 定义,所以也都没有注意过,面对突入起来的问题,还真有点不太清楚为什么有这样的定义.既然不明白就抽时间研究下吧. Java 中的枚举

java 枚举类型enum 的使用

java 枚举类型enum 的使用 最近跟同事讨论问题的时候,突然同事提到我们为什么java 中定义的常量值不采用enmu 枚举类型,而采用public final static 类型来定义呢?以前我们都是采用这种方式定义的,很少采用enum 定义,所以也都没有注意过,面对突入起来的问题,还真有点不太清楚为什么有这样的定义.既然不明白就抽时间研究下吧. Java 中的枚举类型采用关键字enum 来定义,从jdk1.5才有的新类型,所有的枚举类型都是继承自Enum 类型.要了解枚举类型,建议大家先