如何为Java 6枚举实现values()?

如何为Java 6枚举实现values()?,java,enums,Java,Enums,在Java中,可以按如下方式创建枚举: public enum Letter { A, B, C, D, E, F, G; static { for(Letter letter : values()) { // do something with letter } } } 这个问题涉及“values()”方法。具体来说,它是如何实现的?通常,我可以在Eclipse中使用F3或CTRL+Click跳转到Java类的源代码(

在Java中,可以按如下方式创建枚举:

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

    static {
       for(Letter letter : values()) {
          // do something with letter
       }
    }
}
这个问题涉及“values()”方法。具体来说,它是如何实现的?通常,我可以在Eclipse中使用F3或CTRL+Click跳转到Java类的源代码(即使对于String、Character、Integer甚至Enum这样的类也是如此)。可以查看其他枚举方法的源(例如,valueOf(String))

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

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

基本上,编译器(javac)在编译时将枚举转换为包含所有值的静态数组。调用values()时,它将提供此数组的.clone'd()副本

给定此简单枚举:

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);
    }
}
public enum扩展了enum{
/*公共静态最终版本*/COW/*=新材料(“COW”,0)*/,,
/*公共静态最终版本*/POTATO/*=新材料(“POTATO”,1)*/,
/*公共静态final*/鼠标/*=新东西(“鼠标”,2)*/;
/*synthetic*/private static final Stuff[]$VALUES=new Stuff[]{Stuff.COW,Stuff.POTATO,Stuff.MOUSE};
公共静态内容[]值(){
返回(Stuff[])$VALUES.clone();
}
公共静态Stuff值(字符串名称){
return(Stuff)Enum.valueOf(Stuff.class,name);
}
私有内容(/*synthetic*/String$enum$name,/*synthetic*/int$enum$ordinal){
超级($enum$name,$enum$ordinal);
}
}
您可以通过创建一个临时目录并运行以下命令来了解javac如何“翻译”您的类:

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

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

看起来枚举实际上是单例的,因此在整个程序中每个枚举中只能存在一个元素。这使得==运算符对于枚举是合法的

因此,不会出现性能问题,也不会意外更改枚举定义中的某些内容

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

1) 没有。或者至少在当前的实现中没有。有关证据,请参见@lucasmo的答案

2) 阿福,不

假设它可以做到这一点。但是,要证明数组从未在本地修改过,JIT执行起来会很复杂,而且成本相对较高。如果数组从调用
values()
的方法中“转义”,那么它会变得更复杂、更昂贵

这种(假设的)优化很可能不会有回报。。。当平均所有Java代码时

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


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


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

我认为OP意味着修改values()返回的数组。如果这是内部保存的同一个数组对象(而不是副本),那么修改它(例如,将一个元素分配给另一个元素,将null分配给一个元素,等等)会把它搞砸,不仅是对于枚举类,而且对于以后任何对值的调用()是的,您是对的。如果没有克隆,您可以从数组中删除枚举,以后对值的调用将丢失此值。但这只是一个浅拷贝,因为枚举实例是唯一的。System.arraycopy不是更快吗?+1;如果您能解释一下
合成
的含义,那就太好了。为什么
值()
方法
克隆()
数组
$values
?为什么不直接返回它?@RestInPeace,因为数组是可变的。请参阅我不知道的示例
-XD printflat
。了解这些东西的内部运作非常有用。遗憾的是,这件事没有更好的记录。谢谢你让我意识到这一点!