private static final int COLOR_WHITE = Color.WHITE;
private static final int COLOR_BLACK = Color.BLACK;
private static final int COLOR_BLACK = Color.BLACK;
后来才知道原来这样会使得类型不安全,你必须确保是int,而且还要确保它的范围必须正确。
private void updateBackgourndColor(int color){
if (color == COLOR_WHITE || color == COLOR_BLACK)
frameView.setBackgroundColor(color);
}
}
if (color == COLOR_WHITE || color == COLOR_BLACK)
frameView.setBackgroundColor(color);
}
}
像以上的代码,一旦color定义的常量以后增加了,这里就需要相应地修改。而enum可以很方便地解决。
首先看看最简单的enum用法:
public class EnumMonth{
public static void main(String[] args) {
for (Month month : Month.values()) {
System.out.println(month);
}
}
private enum Month {
JAN, SEP, MAR, APR, MAY;
}
}
public static void main(String[] args) {
for (Month month : Month.values()) {
System.out.println(month);
}
}
private enum Month {
JAN, SEP, MAR, APR, MAY;
}
}
JAN
SEP
MAR
APR
MAY
使用javap EnumMonth$Month看到反编译的结果,Month会被编译成一个Java类:
Compiled from "EnumMonth.java"
final class EnumMonth$Month extends java.lang.Enum{
public static final EnumMonth$Month JAN;
public static final EnumMonth$Month SEP;
public static final EnumMonth$Month MAR;
public static final EnumMonth$Month APR;
public static final EnumMonth$Month MAY;
public static EnumMonth$Month[] values();
public static EnumMonth$Month valueOf(java.lang.String);
static {};
}
final class EnumMonth$Month extends java.lang.Enum{
public static final EnumMonth$Month JAN;
public static final EnumMonth$Month SEP;
public static final EnumMonth$Month MAR;
public static final EnumMonth$Month APR;
public static final EnumMonth$Month MAY;
public static EnumMonth$Month[] values();
public static EnumMonth$Month valueOf(java.lang.String);
static {};
}
- 1. 这个Month枚举类是final class,即不能被继承,而它本身是继承于Enum;
- 2. 这些枚举值是Month对象,而且是static final修饰的;
- 3. 注意values()方法,它能得到所有的枚举值,但这方法在Enum里面没有找到,我是在看到反编译结果后才确认的。
而valueOf(String)方法,返回带指定名称的指定枚举类型的枚举常量。在使用javap -c EnumMonth$Month进一步查看汇编代码后,知道原来是调用Enum.valueOf(Class<T> enumType, String name):
public static EnumMonth$Month valueOf(java.lang.String);
Code:
0: ldc_w #4; //class EnumMonth$Month
3: aload_0
4: invokestatic #5; //Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
7: checkcast #4; //class EnumMonth$Month
10: areturn
Code:
0: ldc_w #4; //class EnumMonth$Month
3: aload_0
4: invokestatic #5; //Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
7: checkcast #4; //class EnumMonth$Month
10: areturn
static{}里面主要是new这些枚举值对象。
0: new #4; //class EnumMonth$Month
3: dup
4: ldc #7; //String JAN
6: iconst_0
7: invokespecial #8; //Method "<init>":(Ljava/lang/String;I)V
10: putstatic #9; //Field JAN:LEnumMonth$Month;
3: dup
4: ldc #7; //String JAN
6: iconst_0
7: invokespecial #8; //Method "<init>":(Ljava/lang/String;I)V
10: putstatic #9; //Field JAN:LEnumMonth$Month;
这里要说说Enum的构造函数Enum(String name, int ordinal),形参name就是枚举值的string形式("JAN"),ordinal值则是编译器按照枚举值的顺序从0排着赋值的。要注意,这个ordinal值原来是不能改变的,即编译器是从0升序赋值的,在字节码里面固定写着iconst_0(n=0,1,2...)的。
static{
JAN = new EnumMonth$Month("JAN", 0);
SEP = new EnumMonth$Month("SEP", 1);
...
}
JAN = new EnumMonth$Month("JAN", 0);
SEP = new EnumMonth$Month("SEP", 1);
...
}
看看下面的例子:
public class EnumMonth{
public static void main(String[] args) {
for (Month month : Month.values()) {
System.out.println(month.ordinal());
}
}
private enum Month {
JAN(1), SEP(9), MAR(3), APR(4), MAY(5);
final int id;
Month(int id){
this.id = id;
}
}
}
public static void main(String[] args) {
for (Month month : Month.values()) {
System.out.println(month.ordinal());
}
}
private enum Month {
JAN(1), SEP(9), MAR(3), APR(4), MAY(5);
final int id;
Month(int id){
this.id = id;
}
}
}
1
2
3
4
说明ordinal值是固定不变的。
查看汇编代码:
0: new #4; //class EnumMonth$Month
3: dup
4: ldc #8; //String JAN
6: iconst_0
7: iconst_1
8: invokespecial #9; //Method "<init>":(Ljava/lang/String;II)V
11: putstatic #10; //Field JAN:LEnumMonth$Month;
3: dup
4: ldc #8; //String JAN
6: iconst_0
7: iconst_1
8: invokespecial #9; //Method "<init>":(Ljava/lang/String;II)V
11: putstatic #10; //Field JAN:LEnumMonth$Month;
我再尝试把int id改为char,运行结果是一样的:
private enum Month {
JAN('1'), SEP('9'), MAR('3'), APR('4'), MAY('5');
final char id;
Month(char id){
this.id = id;
}
}
JAN('1'), SEP('9'), MAR('3'), APR('4'), MAY('5');
final char id;
Month(char id){
this.id = id;
}
}
但编译代码有所不同:
0: new #4; //class EnumMonth$Month
3: dup
4: ldc #8; //String JAN
6: iconst_0
7: bipush 49
9: invokespecial #9; //Method "<init>":(Ljava/lang/String;IC)V
12: putstatic #10; //Field JAN:LEnumMonth$Month;
3: dup
4: ldc #8; //String JAN
6: iconst_0
7: bipush 49
9: invokespecial #9; //Method "<init>":(Ljava/lang/String;IC)V
12: putstatic #10; //Field JAN:LEnumMonth$Month;
Enum(String name, int ordinal, char id) {
super(name, ordinal);
//copy 自定义构造函数的内容
this.id = id;
}
super(name, ordinal);
//copy 自定义构造函数的内容
this.id = id;
}
现在了解了枚举值就是该枚举类的对象,可以传常量数值作为枚举对象的属性以及自定义构造函数,接下来看看我们平时常用的switch例子:
switch(color) {
case RED:
System.out.println("红色");
break;
case GREEN:
System.out.println("绿色");
break;
case BLACK:
System.out.println("黑色");
break;
...
default:
break;
}
case RED:
System.out.println("红色");
break;
case GREEN:
System.out.println("绿色");
break;
case BLACK:
System.out.println("黑色");
break;
...
default:
break;
}
实际上用以下代码可以解决,每个枚举对象都包含desc属性值和getDesc()方法:
public class EnumColor {
public static void main(String[] args) {
for (Color color : Color.values())
System.out.println(color + " is : " + color.getDesc());
}
private enum Color {
RED("红色"), GREEN("绿色"), BLUE("蓝色"), YELLOW("黄色"), BLACK("黑色"), WHITE("白色");
private final String desc;
private Color(String desc) {
this.desc = desc;
}
public String getDesc() {
return desc;
}
}
}
public static void main(String[] args) {
for (Color color : Color.values())
System.out.println(color + " is : " + color.getDesc());
}
private enum Color {
RED("红色"), GREEN("绿色"), BLUE("蓝色"), YELLOW("黄色"), BLACK("黑色"), WHITE("白色");
private final String desc;
private Color(String desc) {
this.desc = desc;
}
public String getDesc() {
return desc;
}
}
}
运行结果:
RED is : 红色
GREEN is : 绿色
BLUE is : 蓝色
YELLOW is : 黄色
BLACK is : 黑色
WHITE is : 白色
以上的例子提醒我们,不需要在外面的代码添加switch逻辑来判断以赋予不同的值,直接在enum里面处理就完成了。
注意:在case标签中,枚举前缀不能出现,即case Color.RED是不合法的,只能直接用枚举值RED。而在其他地方出现时则必须用Color.RED。
为什么switch可以支持enum呢?switch其实是支持int基本类型,而因为byte,short,char可以向上转换为int,所以switch也支持它们,但long因为转换int会截断便不能支持。
而enum在switch中也是int类型,看了《switch之enum》,应该这样解释:
我使用jd反编译上面那段switch代码:
??? = EnumColor.Color.RED;
switch (EnumColor.1.$SwitchMap$EnumColor$Color[???.ordinal()]) {
case 1:
System.out.println("红色");
break;
case 2:
System.out.println("绿色");
break;
case 3:
System.out.println("黑色");
break;
}
switch (EnumColor.1.$SwitchMap$EnumColor$Color[???.ordinal()]) {
case 1:
System.out.println("红色");
break;
case 2:
System.out.println("绿色");
break;
case 3:
System.out.println("黑色");
break;
}
ai[Color.RED.ordinal()] = 1;
ai[Color.GREEN.ordinal()] = 2;
...
再看看switch括号里面的就是数组[???.ordinal()],明了!
下面说一下方法枚举(多态),我一开始写的是以下代码:
public class EnumGrade {
public static void main(String[] args) {
for (Grade grade : Grade.values())
System.out.println(grade + "'s result is : " + grade.getResult());
}
private enum Grade {
A(1) {
public int getResult() {
return base;
}
},
B(3) {
public int getResult() {
return base*2;
}
},
C(5) {
public int getResult() {
return base*3;
}
};
private final int base;
private Grade(int base) {
this.base = base;
}
public abstract int getResult();
}
}
public static void main(String[] args) {
for (Grade grade : Grade.values())
System.out.println(grade + "'s result is : " + grade.getResult());
}
private enum Grade {
A(1) {
public int getResult() {
return base;
}
},
B(3) {
public int getResult() {
return base*2;
}
},
C(5) {
public int getResult() {
return base*3;
}
};
private final int base;
private Grade(int base) {
this.base = base;
}
public abstract int getResult();
}
}
EnumGrade.java:10: 无法从静态上下文中引用非静态 变量 base
return base;
^
EnumGrade.java:16: 无法从静态上下文中引用非静态 变量 base
return base*2;
^
EnumGrade.java:22: 无法从静态上下文中引用非静态 变量 base
return base*3;
^
于是修改为以下代码:
public class EnumGrade {
public static void main(String[] args) {
for (Grade grade : Grade.values())
System.out.println(grade + "'s result is : " + grade.getResult(grade.ordinal()));
}
private enum Grade {
A {
public int getResult(int base) {
return base;
}
},
B {
public int getResult(int base) {
return base*2;
}
},
C {
public int getResult(int base) {
return base*3;
}
};
public abstract int getResult(int base);
}
}
public static void main(String[] args) {
for (Grade grade : Grade.values())
System.out.println(grade + "'s result is : " + grade.getResult(grade.ordinal()));
}
private enum Grade {
A {
public int getResult(int base) {
return base;
}
},
B {
public int getResult(int base) {
return base*2;
}
},
C {
public int getResult(int base) {
return base*3;
}
};
public abstract int getResult(int base);
}
}
A's result is : 0
B's result is : 2
C's result is : 6
B's result is : 2
C's result is : 6
另外Java1.5还有EnumMap和EnumSet(参考Java枚举类型enum使用详解):
运行结果:
import java.util.EnumMap;
import java.util.EnumSet;
public class EnumState{
public static void main(String[] args) {
// EnumSet的使用
EnumSet<State> stateSet = EnumSet.allOf(State.class);
for (State s : stateSet) {
System.out.println(s);
}
// EnumMap的使用
EnumMap<State,String> stateMap = new EnumMap<State,String>(State.class);
stateMap.put(State.ON, "is On");
stateMap.put(State.OFF, "is off");
for (State s : State.values()) {
System.out.println(s.name() + ":" + stateMap.get(s));
}
}
private enum State {
ON, OFF
};
}
import java.util.EnumSet;
public class EnumState{
public static void main(String[] args) {
// EnumSet的使用
EnumSet<State> stateSet = EnumSet.allOf(State.class);
for (State s : stateSet) {
System.out.println(s);
}
// EnumMap的使用
EnumMap<State,String> stateMap = new EnumMap<State,String>(State.class);
stateMap.put(State.ON, "is On");
stateMap.put(State.OFF, "is off");
for (State s : State.values()) {
System.out.println(s.name() + ":" + stateMap.get(s));
}
}
private enum State {
ON, OFF
};
}
ON
OFF
ON:is On
OFF:is off
*****因为本人使用javap和jd也得不到更好的反编译代码,某些反编译结果的分析是结合网上的文章和自己看到的反编译结果推理所得。
本文转自 Icansoft 51CTO博客,原文链接:
http://blog.51cto.com/android/563950