Java中枚举的梗
2017-01-04 13:54
405 查看
Java中的枚举定义和使用,相信大多数的java程序员都会,但是对于enum的使用其实很多人还是一知半解,包括本人。自己无意当中看到下面这段程序后,发现自己彻底懵了,问了一些朋友,知道的也不多,突然觉得自己对枚举的认知量太少。通过学习JLS的enum types的规范后,总结了关于枚举的相关内容。
对于上面这段程序,我有三点疑问:
1. PLUS、MINUS、TIMES、DIVIDED_BY后面的{……}的内容?
2. 枚举中的抽象方法abstract d eval(d,d)?
3. 枚举中的main主函数?
枚举不仅有类修饰符,还可以实现接口,哇,多么有意思!下面我们一个个的进行了解。
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
枚举作为一种特殊的类,具备了类可以实现接口的基本特性。将上面的代码进行稍加修改后如下:
上述程序中的PLUS、MINUS、TIMES和DIVIDED_BY是枚举常量,这里很特殊,因为它不同于class中的字段定义特性,它是枚举中规定的语法特性。为什么要称为常量,而不是枚举字段或者什么?呵呵,看下javap打印出来的内容,会发现小惊喜。
正是public static final修饰符对PLUS、MINUS、TIMES和DIVIDED_BY这四个类字段进行了修饰,在类中称为常量,在枚举中称为枚举常量。同时,还可以看到这四个常量的类型为Operation,那么是否也可以理解了第一个问题:{……}是个匿名内部类。
这个做了什么?创建一个匿名类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
总结
对于枚举,它是一种特殊的class,其内部可以包含了常量、字段、方法、内部类和接口,并且一般类中对上述内容的定义形式和枚举可能不同。那么,在实际使用枚举的过程中,可以增加一些看似怪异,实际允许的代码,对其进行分析时,牢记class即可。
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即可。
相关文章推荐
- JAVA 枚举
- Java中使用枚举
- Java中如何由枚举常量的ordinal值获得枚举常量对象
- 简述java枚举类型
- java中的枚举
- java枚举
- 定义java的枚举变量
- Java中的枚举类型
- 9.Java中的集合、枚举、泛型【下】
- java新功能之一枚举
- 再谈在Java中使用枚举
- Java基础-枚举的两种基本模式
- 深入探讨Java枚举的实现
- Java中枚举详解(1)
- Java枚举,酷! (原文发表于2006-04-25 下午01:13:48)
- java类型安全枚举与反射机制相结合的应用 作者:封宇
- 关于Collection.sort和java的枚举
- Java中如何由枚举常量的ordinal值获得枚举常量对象
- Java枚举工具类ValuedEnum用法
- JavaAnnotation与枚举