Enum不需要重新编译其客户 - 如何?

时间:2013-07-11 11:47:42

标签: java enums

来自Effective Java

  

您可以在enum类型中添加或重新排序常量而无需重新编译其客户端,因为导出常量的字段在enum类型与其客户端之间提供了一层绝缘:常量值不是按照int enum模式编译到客户端。

我从this link了解到int enum patterns是编译时常量。我想知道的是enum内部如何运作?

2 个答案:

答案 0 :(得分:3)

枚举不是编译时常量。因此,编译器不会将它们的值复制到使用它们的每个类。这与int值不同,int值可以是编译时常量。

所以,如果你有一个像

这样的课程
public class Constants {
    public static final int FOO = 1;
}

并有另一个班级

public class Client {
    public static void main(String[] args) {
        System.out.println(Constants.FOO);
    }
}

该课程将打印1.现在将FOO的声明更改为

public static final int FOO = 27;

重新编译常量而不重新编译客户端。执行客户端。打印值仍为1,因为在编译Client类时,编译器已将其复制到Client类。

使用枚举无法获得此效果。如果将值存储为枚举的实例变量,或者如果引用其序号(),则即使不重新编译Client类,也始终可以获得正确的值。

答案 1 :(得分:1)

重要的是要了解枚举是按名称引用的。

假设我们有一个枚举:

package com.example;

public enum MyEnum {
    ONE, TWO, THREE
}

一个简单的测试类:

package com.example;

public class EnumTest {
    public static void main(String[] args) {
        System.out.println(MyEnum.TWO);
    }
}

现在让我们使用javap命令行工具反编译测试类:

javap -verbose -c com/example/EnumTest产生这个(为简洁而截断):

Compiled from "EnumTest.java"
...
const #22 = Field   #23.#25;    //  com/example/MyEnum.TWO:Lcom/example/MyEnum;
const #23 = class   #24;    //  com/example/MyEnum
const #24 = Asciz   com/example/MyEnum;
const #25 = NameAndType #26:#27;//  TWO:Lcom/example/MyEnum;
const #26 = Asciz   TWO;
const #27 = Asciz   Lcom/example/MyEnum;;
...

{
public static void main(java.lang.String[]);
  Code:
   Stack=2, Locals=1, Args_size=1
   0:   getstatic   #16; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:   getstatic   #22; //Field com/example/MyEnum.TWO:Lcom/example/MyEnum;
   6:   invokevirtual   #28; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V
   9:   return
}

请注意,此代码引用常量#22,后者又间接引用#26,这是ASCII字符串“TWO”。

因此,只要MyEnum的类名保持不变并且实例TWO的名称未更改,就不必重新编译类EnumTest