Как values () реализован для перечислений Java 6?
В 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 ответа
По сути, компилятор (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
Если вы присваиваете его локальной переменной, единственное, что вы можете изменить, - это присвоить этой переменной другое перечисление. Это не изменит самого перечисления, потому что вы меняете только объект, на который ссылается ваша переменная.
Кажется, что перечисления на самом деле являются одиночными, поэтому в вашей программе может существовать только один элемент из каждого перечисления, что делает оператор == допустимым для перечислений.
Таким образом, нет проблем с производительностью, и вы не можете случайно что-то изменить в своем определении enum.
Код для него нативный? Или JVM / компилятор обрабатывает это специально, возвращая новый экземпляр из values () только тогда, когда он не может доказать, что он не будет изменен.
1) Нет. Или, по крайней мере, не в текущих реализациях. Смотрите ответ @lucasmo для доказательства.
2) AFAIK, нет.
Гипотетически это могло бы сделать это. Однако доказать, что массив никогда не изменяется локально, было бы сложно и относительно дорого для выполнения JIT. Если массив "выходит" из метода, который вызвал values()
это становится сложнее и дороже.
Скорее всего, эта (гипотетическая) оптимизация не окупится... при усреднении по всему коду Java.
Другая проблема заключается в том, что эта (гипотетическая) оптимизация может открыть дыры в безопасности.
Интересно то, что JLS, кажется, не указывает, что values()
элемент возвращает копию массива. Здравый смысл 1 говорит, что он должен делать... но это на самом деле не указано.
1 - Это будет зияющая дыра в безопасности, если values()
вернул общий (изменяемый) массив enum
ценности.