Java中枚举的梗

来源:互联网 发布:crm软件有哪些 编辑:程序博客网 时间:2024/05/01 16:43

Java中的枚举定义和使用,相信大多数的java程序员都会,但是对于enum的使用其实很多人还是一知半解,包括本人。自己无意当中看到下面这段程序后,发现自己彻底懵了,问了一些朋友,知道的也不多,突然觉得自己对枚举的认知量太少。通过学习JLS的enum types的规范后,总结了关于枚举的相关内容。

enum Operation {    PLUS {        double eval(double x, double y) {            return x + y;        }    },    MINUS {        double eval(double x, double y) {            return x - y;        }    },    TIMES {        double eval(double x, double y) {            return x * y;        }    },    DIVIDED_BY {        double eval(double x, double y) {            return x / y;        }    };    /**     * 如果枚举中定义了抽象方法,那么所有的枚举常量中都必须实现     */    abstract double eval(double x, double y);    public static void main(String[] args) {        double x = Double.parseDouble(args[0]);        double y = Double.parseDouble(args[1]);        for(Operation p : Operation.values()) {            System.out.println(x + " " + p + " " + y + " = " + p.eval(x, y));        }    }}

对于上面这段程序,我有三点疑问:
1. PLUS、MINUS、TIMES、DIVIDED_BY后面的{……}的内容?
2. 枚举中的抽象方法abstract d eval(d,d)?
3. 枚举中的main主函数?

一、enum类型是什么?

枚举是一类可以列举的数的集合,这里面包含两类信息:1、数的类型相同;2、数的个数确定。例如,交通信号灯可以定义为一个枚举,包含RED、YELLOW、GREEN。在java中是通过关键字enum在定义时进行说明该类型是枚举类型。在JLS中是如何定义枚举呢?“An enum declaration specifies a new enum type, a special kind of class type. ”,枚举的申明指定了一个新的枚举类型,该枚举类型是一种特殊的class类型,它的申明形式为:

EnumDeclaration:{ClassModifier} enum Identifier [Superinterfaces] EnumBody 

枚举不仅有类修饰符,还可以实现接口,哇,多么有意思!下面我们一个个的进行了解。

1. ClassModifier

我们知道类的修饰符可以为public、protected、private、abstract、final、static、strictfp;那么枚举作为特殊的class,在访问修饰符上有哪些特殊呢?
首先,同一个修饰符不能修饰两次;
其次,abstract和final不能修饰枚举类,这其中包含两个了隐士问题:枚举定义中的枚举常量,也即RED、GREEN和YELLOW,都是当前枚举的实例对象,也即枚举可以进行实例化,如果使用abstract进行显示的定义为抽象类,则违反了抽象类不能实例化规范;如果枚举定义中的枚举常量后面没有{ ……. }, 那么编译器默认会给当前枚举加上final关键字进行修饰,如果显示的进行了定义,那么就相当于final修饰符出现了两次,违反了修饰符的限制。
然后,static修饰符在枚举作为class的内部类型时可进行修饰,但是它默认为static,这也隐士的说明enum不能出现在inner class内部,因为内部类中不能单独定义static成员,除非static final成员。

2. Superinterfaces

枚举作为一种特殊的类,具备了类可以实现接口的基本特性。将上面的代码进行稍加修改后如下:

enum Operation implements IArithmetic{    PLUS {        double eval(double x, double y) {            return x + y;        }    },    MINUS {        double eval(double x, double y) {            return x - y;        }    },    TIMES {        double eval(double x, double y) {            return x * y;        }    },    DIVIDED_BY {        double eval(double x, double y) {            return x / y;        }    };    /**     * 如果枚举中定义了抽象方法,那么所有的枚举常量中都必须实现     */    abstract double eval(double x, double y);    public static void main(String[] args) {        IArithmetic arithmetic = Operation.PLUS;        double x = Double.parseDouble(args[0]);        double y = Double.parseDouble(args[1]);        for(Operation p : Operation.values()) {            System.out.println(x + " " + p + " " + y + " = " + p.eval(x, y));        }    }}interface IArithmetic {}

二、EnumBody 可以包含哪些内容?

1、Enum Contants枚举常量

上述程序中的PLUS、MINUS、TIMES和DIVIDED_BY是枚举常量,这里很特殊,因为它不同于class中的字段定义特性,它是枚举中规定的语法特性。为什么要称为常量,而不是枚举字段或者什么?呵呵,看下javap打印出来的内容,会发现小惊喜。

Compiled from "Operation.java"abstract class Operation extends java.lang.Enum implements IArithmetic {    public static final Operation PLUS;    public static final Operation MINUS;    public static final Operation TIMES;    public static final Operation DIVIDED_BY;    public static final Operation[] values();    public static Operation valueOf(java.lang.String);    abstract double eval(double, double);    public static void main(java.lang.String[]);    Operation(java.lang.String, int, Operation$1);    static {};}

正是public static final修饰符对PLUS、MINUS、TIMES和DIVIDED_BY这四个类字段进行了修饰,在类中称为常量,在枚举中称为枚举常量。同时,还可以看到这四个常量的类型为Operation,那么是否也可以理解了第一个问题:{……}是个匿名内部类。

PLUS {        double eval(double x, double y) {            return x + y;        }    }

这个做了什么?创建一个匿名类Operation$1.class,该类继承了Operation,同时实例化该类的对象,并且将其赋值给PLUS常量,也就是将Operation$1子类对象赋值给父类Operation引用。
说明
首先,枚举常量的申明形式为:
EnumConstant:
{EnumConstantModifier} Identifier [( [ArgumentList] )] [ClassBody]
这里EnumConstantModifier为Annotation注解,不是修饰符;
其次,Identifier [( [ArgumentList] )]说明枚举常量的形式可以为PLUS或者PLUS(args),也即可以传递参数,那么传递给谁呢?传递给构造函数。如果PLUS形式,则默认是无参数的构造函数,如果有参数,则调用override的构造函数;
然后,[ClassBody]是一个匿名类,该匿名类extends了外部包装enum类型,如果override外部enum中定义的方法,那么引用了该枚举常量的程序,可以访问到该方法;
最后,枚举常量对于每一枚举类型都是单一的,也就是说它们的比较可以用”==”来进行。

2、Enum Body申明

枚举是一个特殊的class,而在class中可以定义字段、方法、内部类和接口,枚举中也具备这些功能。那枚举又和class有什么区别呢?

  • 构造函数constructor
    首先,public、protected不能作为构造函数的修饰符,同时构造函数如果没有修饰符默认为private,也即是说有没有修饰符,枚举的构造函数都是private;枚举extends了java.lang.Enum类,所以枚举中也具有super关键字,但是在构造函数中不能显示的调用super(…)来构造java.lang.Enum实例;最后,构造函数、实例初始化器和实例字段的设值器中不能引用static字段。
  • 方法
    首先,方法可以是实例方法、类方法、抽象方法,也即是可以添加static、abstract修饰符来修饰方法;其次,编译器会默认给枚举添加values、valueof两个静态方法;其次,Enum<E>中的方法中,枚举也继承了,但是其中的方法加了final修饰符;最后,枚举中定义了abstract方法,那么所有的枚举常量都必须实现该抽象方法,也即前面程序所示形式。


    总结
    对于枚举,它是一种特殊的class,其内部可以包含了常量、字段、方法、内部类和接口,并且一般类中对上述内容的定义形式和枚举可能不同。那么,在实际使用枚举的过程中,可以增加一些看似怪异,实际允许的代码,对其进行分析时,牢记class即可。

0 0
原创粉丝点击