您的位置:首页 > 编程语言 > Java开发

Java中枚举的梗

2017-01-04 13:54 405 查看
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即可。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: