如何为 Java6枚举实现 value() ?

在 Java 中,您可以创建如下枚举:

public enum Letter {
A, B, C, D, E, F, G;


static {
for(Letter letter : values()) {
// do something with letter
}
}
}

这个问题涉及到“ value ()”方法。具体来说,它是如何实现的?通常,我可以在 Eclipse 中使用 F3或 CTRL + C 单击跳转到 Java 类的源代码(甚至可以跳转到类 String、 Property、 Integer,甚至 Enum)。可以查看其他枚举方法(例如 valueOf (String))的源。

“ values ()”是否在每次调用时都创建一个新数组?如果我将它分配给一个局部变量,然后修改其中一个元素,会发生什么(显然这不会影响 value ()返回的值,这意味着每次都会分配一个新数组)。

它的代码是本地的吗?或者 JVM/编译器对它进行特殊处理,只在无法证明它不会被修改时从 value ()返回一个新实例。

19279 次浏览

基本上,编译器(javac)在编译时将枚举转换为包含所有值的静态数组。当您调用 value ()时,它会给您一个。这个数组的克隆()副本。

考虑到这个简单的枚举:

public enum Stuff {
COW, POTATO, MOUSE;
}

你可以看看 Java 生成的代码:

public enum Stuff extends Enum<Stuff> {
/*public static final*/ COW /* = new Stuff("COW", 0) */,
/*public static final*/ POTATO /* = new Stuff("POTATO", 1) */,
/*public static final*/ MOUSE /* = new Stuff("MOUSE", 2) */;
/*synthetic*/ private static final Stuff[] $VALUES = new Stuff[]{Stuff.COW, Stuff.POTATO, Stuff.MOUSE};


public static Stuff[] values() {
return (Stuff[])$VALUES.clone();
}


public static Stuff valueOf(String name) {
return (Stuff)Enum.valueOf(Stuff.class, name);
}


private Stuff(/*synthetic*/ String $enum$name, /*synthetic*/ int $enum$ordinal) {
super($enum$name, $enum$ordinal);
}
}

通过创建一个临时目录并运行以下命令,您可以了解 javac 是如何‘翻译’您的类的:

javac -d <output directory> -XD-printflat filename.java

如果将其分配给局部变量,那么唯一可以修改的是将另一个枚举分配给该变量。这不会更改枚举本身,因为您只是更改变量引用的对象。

似乎枚举实际上是单例的,所以每个枚举中只有一个元素可以存在于整个程序中,这使得 = = 操作符对枚举是合法的。

所以不存在性能问题,并且您不能意外地更改枚举定义中的某些内容。

它的代码是本地的吗?或者 JVM/编译器对它进行特殊处理,只在无法证明它不会被修改时从 value ()返回一个新实例。

1)没有。或者至少在目前的实现中没有。见@lucasmo 的证据答案。

2) AFAIK,不。

假设它可以做到这一点。然而,证明一个数组从未在本地被修改对于 JIT 来说是相当复杂和昂贵的。如果数组从调用 values()的方法中“逃脱”,那么它就会变得更加复杂和昂贵。

当在所有 Java 代码中取平均值时,这种(假设的)优化可能不会得到回报。

另一个问题是,这种(假设的)优化可能会打开安全漏洞。


但有趣的是,JLS 似乎没有指定 values()成员返回数组副本。常识 1说它 必须的做... 但它实际上没有指定。

如果 values()返回 enum值的共享(可变)数组,这将是一个巨大的安全漏洞。