Java枚举使用方式和注意事项

来源:互联网 发布:网络专线多少钱一年 编辑:程序博客网 时间:2024/06/08 12:49

枚举类型 是一组固定常量组成合法值的类型,比如一年中的季节,一周中的星期。

int枚举模式

不使用枚举时定义常量的方法通常是这样:

public final class Week {    public static final int WEEK_MONDAY = 1;    public static final int WEEK_TUESDAY = 2;    public static final int WEEK_WEDNESDAY = 3;    public static final int WEEK_THURSDAY = 4;    public static final int WEEK_FRIDAY = 5;    public static final int WEEK_SATURDAY = 6;    public static final int WEEK_SUNDAY = 7;}

这种方法称作int枚举模式,这种方式有些缺点:

  • 类型非安全,不能在编译期间发现问题,比如setWeek(10)或者setWeek(MONTH_JANUARY)在语法上都是合法的,但你的本意并非如此。
  • 可读性差,缺乏方便的方法把常量转换为有意义的字符串显示。

java1.5开始java提供一种新的引用类型 – 枚举类型(enum type)

定义枚举类型以及简单使用

/*** 定义了加、减、乘、除 操作*/public enum Operation {  PLUS, MINUS, TIMES, DIVIDE}
//计算并打印结果public static void calc(int num1, int num2, Operation op) {    switch (op) {        case PLUS:            System.out.println(num1 + " + " + num2 + " = "+num1 + num2);            break;        case MINUS:            System.out.println(num1 - num2);            break;        case TIMES:            System.out.println(num1 * num2);            break;        case DIVIDE:            System.out.println(num1 / num2);            break;        default:            throw new RuntimeException();    }}  //调用计算方法  calc(1, 2, Operation.PLUS);//输出: 1 + 2 = 12

其中的PLUS MINUS等都是Operation类型,因此可以避免上面int枚举模式中类型安全问题,
可以利用编译器检查出类型不匹配。

枚举的一些特点

使用javap反编译上面的Operation.class得到下面代码

public final class com.erick.hello.Operation extends java.lang.Enum<com.erick.hello.Operation> {    public static final com.erick.hello.Operation PLUS;    public static final com.erick.hello.Operation MINUS;    public static final com.erick.hello.Operation TIMES;    public static final com.erick.hello.Operation DIVIDE;    static {};    public static com.erick.hello.Operation[] values();    public static com.erick.hello.Operation valueOf(java.lang.String);}

通过上面反编译的结果可以知道:

  • Operation隐式继承了java.lang.EnumEnum实现了Comparable<E> Serializable 这两个接口。
  • Operation不能再继承其他类,但可以实现其他接口。
  • Operation是final的,所以不能被扩展。但这不是真正原因,真正的限制应该是编译器做的限制。
  • 新生成了两个方法values() valueOf(String)
  • 定义的每一个枚举值实际上都是一个Operation类型的实例

除了完善了int枚举类型的不足之外,枚举类型还可以添加任意的方法和属性,并且可以实现任意接口。
添加任意方法的原因可能是想要将数据或者操作与对应的常量关联。

为枚举增加属性和方法

/** * 定义了加、减、乘、除 操作 */public enum Operation {    //枚举常量的定义必须在第一行,最后一个常量后面加分号,后面是定义的属性和方法    PLUS("+"), MINUS("-"), TIMES("*"), DIVIDE("/");    private String op;    private Operation(String op) {        this.op = op;    }    public String getOP() {        return op;    }    public void calc(int num1, int num2) {        switch (this) {        case PLUS:            System.out.println(num1 + op + num2 + " = " + (num1 + num2));            break;        case MINUS:            System.out.println(num1 - num2);            break;        case TIMES:            System.out.println(num1 * num2);            break;        case DIVIDE:            System.out.println(num1 / num2);            break;        default:            throw new RuntimeException();        }    }}//测试Operation.PLUS.calc(1, 2);//输出:1+2 = 3

上面的例子中如果想要增加一个开方运算,就需要增加一个开方枚举,并在switch case 中加一个分支来做具体运算。
但是这样很容易只添加了开方枚举,但忘记了具体运算的实现,而且能够顺利编译。下面的方法可以尽量避免这一情况。

public enum Operation {    PLUS("+") {        @Override        void calc(int num1, int num2) {            System.out.println(num1 + this.getOP() + num2 + " = " + (num1 + num2));        }    },    MINUS("-") {        @Override        void calc(int num1, int num2) {            System.out.println(num1 + this.getOP() + num2 + " = " + (num1 + num2));        }    };    private String op;    private Operation(String op) {        this.op = op;    }    public String getOP() {        return op;    }    abstract void calc(int num1, int num2);}

枚举中通用的方法

//比较此枚举与指定对象的顺序。int compareTo(E o) //返回与此枚举常量的枚举类型相对应的 Class 对象。Class<E> getDeclaringClass() //返回此枚举常量的名称,在其枚举声明中对其进行声明。String name() //与name()返回一致String toString()//返回枚举常量的序数(它在枚举声明中的位置,其中初始常量序数为零)。int ordinal() //返回带指定名称的指定枚举类型的枚举常量。static <T extends Enum<T>> T valueOf(String name) //测试for(Operation o : Operation.values()){    System.out.println(o.ordinal() + " : " + o.name() + "  : " + o.getDeclaringClass());}// 输出://  0 : PLUS  : class com.erick.hello.Operation//  1 : MINUS  : class com.erick.hello.Operation//  2 : TIMES  : class com.erick.hello.Operation//  3 : DIVIDE  : class com.erick.hello.Operation

ordinal()的使用

大多数情况下不需要使用这个方法,这个方法的设计是用于像EnumSet和EnumMap这种基于枚举通用数据结构的,
除非是编写这些数据结构,否则应该避免使用ordinal方法。

如果需要枚举与一个int值绑定,应该使用枚举实例的属性来绑定。

public enum Week {    WEEK_MONDAY(1), WEEK_TUESDAY(2);    private int num;    Week(int num) {        this.num = num;    }}

使用接口模拟可伸缩的枚举

枚举类型不能被继承,这是语言特性,而且枚举是用来表示有限数量的对象,因此大多情况下并不需要扩展。
但有些情况,比如一些特特定操作,当下只包括了所有操作的一部分就可以满足需求,
但需要更多操作时就需要扩展。

上面的计算器例子中,实现了计算器的加、减、乘、除操作,但之后又需要一个求幂的操作,
这时可以实现一个操作接口,来实现可扩展的特性。

//操作接口public interface Operation {    void calc(int num1, int num2);}//基础计算枚举public enum BasicOperation implements Operation{    //....省略了减法和乘除。    PLUS("+") {        @Override        public void calc(int num1, int num2) {            System.out.println(num1 + this.getOP() + num2 + " = " + (num1 + num2));        }    };    private String op;    private BasicOperation(String op) {        this.op = op;    }    public String getOP() {        return op;    }}//扩展一个幂运算public enum ExtendedOperation implements Operation{    PLUS("^") {        @Override        public void calc(int num1, int num2) {            System.out.println(num1 + this.getOP() + num2 + " = " + Math.pow(num1, num2));        }    };    private String op;    private ExtendedOperation(String op) {        this.op = op;    }    public String getOP() {        return op;    }}

只要API通过接口的方式操作,就可以允许客户端扩展自己的操作方式。

1 0
原创粉丝点击