【问题标题】:Enum requires no recompilation of its clients - How?Enum 不需要重新编译其客户端 - 如何?
【发布时间】:2013-07-11 11:47:41
【问题描述】:

来自有效的 Java

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

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

【问题讨论】:

    标签: java enums


    【解决方案1】:

    枚举不是编译时常量。所以它们的值不会被编译复制到每个使用它们的类。这与 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类了。

    没有办法使用枚举来产生这种效果。如果您将值存储为枚举的实例变量,或者如果您引用它的 ordinal(),您将始终获得正确的值,即使不重新编译 Client 类。

    【讨论】:

    • 现在应该清楚为什么不应该依赖枚举的.ordinal() 值了:它可能会改变。
    【解决方案2】:

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

    假设我们有一个枚举:

    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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-09
      • 2011-03-07
      • 2014-07-16
      • 1970-01-01
      相关资源
      最近更新 更多