Как values ​​() реализованы для перечислений Java 6? - PullRequest
65 голосов
/ 22 июля 2009

В Java вы можете создать перечисление следующим образом:

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

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

Этот вопрос касается метода «values ​​()». В частности, как это реализовано? Обычно я могу перейти к исходному коду для классов Java, используя F3 или CTRL + Click в Eclipse (даже для таких классов, как String, Character, Integer и даже Enum). Можно просмотреть источник других методов перечисления (например, valueOf (String)).

«values ​​()» создает новый массив каждый раз, когда он вызывается? Если я назначу его локальной переменной, а затем изменю один из элементов, что произойдет (очевидно, это не повлияет на значение, возвращаемое values ​​(), что означает, что каждый раз выделяется новый массив).

Код для него нативный? Или JVM / компилятор обрабатывает его специально, возвращая новый экземпляр из values ​​() только тогда, когда он не может доказать, что он не будет изменен.

Ответы [ 3 ]

105 голосов
/ 22 июля 2009

По сути, компилятор (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);
    }
}

Вы можете посмотреть, как javac «переводит» ваши классы, создав временный каталог и запустив:

javac -d <output directory> -XD-printflat filename.java
2 голосов
/ 22 июля 2009

Если вы присваиваете ее локальной переменной, единственное, что вы можете изменить, - это присвоить этой переменной другое перечисление. Это не изменит самого перечисления, потому что вы меняете только объект, на который ссылается ваша переменная.

Кажется, что перечисления на самом деле являются одиночными, поэтому в вашей программе может существовать только один элемент из каждого перечисления, что делает оператор == допустимым для перечислений.

То есть проблем с производительностью нет, и вы не можете случайно что-то изменить в своем определении enum.

1 голос
/ 23 января 2016

Код для него нативный? Или JVM / компилятор обрабатывает его специально, возвращая новый экземпляр из values ​​(), только когда он не может доказать, что он не будет изменен.

1) Нет. Или, по крайней мере, не в текущих реализациях. См. Ответ @ lucasmo для доказательства.

2) AFAIK, нет.

Гипотетически это могло бы сделать это. Однако доказать, что массив никогда не изменяется локально, было бы сложно и относительно дорого для выполнения JIT. Если массив «выходит» из метода, который вызвал values(), он становится более сложным и более дорогим.

Скорее всего, эта (гипотетическая) оптимизация не окупится ... при усреднении по всему Java-коду.

Другая проблема заключается в том, что эта (гипотетическая) оптимизация может открыть дыры в безопасности.


Интересно то, что JLS, по-видимому, не указывает, что член values() возвращает копию массива. Здравый смысл 1 говорит, что должен сделать ... но на самом деле это не указано.

1 - Это была бы дыра в безопасности, если бы values() вернул общий (изменяемый) массив значений enum.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...