Перечисление Java и дополнительные файлы классов
Я заметил:enums введите много дополнительных файлов класса (Класс$1) после компиляции раздувания общего размера. Он, кажется, прикреплен к каждому классу, который даже использует перечисление, и они часто дублируются.
почему это происходит и есть ли способ предотвратить это без удаления перечисления.
(причина вопроса-это пространство для меня)
EDIT
при дальнейшем изучении вопроса, Солнце Javac 1.6 создает дополнительный синтетический класс каждый раз, когда вы используете переключатель на перечислении. Он использует какой-то SwitchMap. этой сайт имеет дополнительную информацию, и здесь говорит вам, как анализировать то, что Javac делает.
дополнительный физический файл кажется высокой ценой, чтобы заплатить каждый раз, когда вы используете переключатель на перечисление!
интересно, что компилятор Eclipe не создает эти дополнительные файлы. Интересно, есть ли единственное решение переключать компиляторы?
5 ответов:
Я был просто бит этим поведением, и этот вопрос появился при Гугле. Я подумал, что поделюсь немного дополнительной информацией, которую я узнал.
javac 1.5 и 1.6 создают дополнительный синтетический класс каждый раз, когда вы используете переключатель на перечислении. Класс содержит так называемую" карту переключения", которая отображает индексы перечисления для переключения номеров переходов таблицы. Важно отметить, что синтетический класс создается для класса, в котором происходит переключение,не перечисление класс.
вот пример того, что генерируется:
EnumClass.java
public enum EnumClass { VALUE1, VALUE2, VALUE3 }перечислитель.java
public class EnumUser { public String getName(EnumClass value) { switch (value) { case VALUE1: return "value 1"; // No VALUE2 case. case VALUE3: return "value 3"; default: return "other"; } } }Синтетический Перечислитель$1.класс
class EnumUser { static final int[] $SwitchMap$EnumClass = new int[EnumClass.values().length]; static { $SwitchMap$EnumClass[EnumClass.VALUE1.ordinal()] = 1; $SwitchMap$EnumClass[EnumClass.VALUE3.ordinal()] = 2; }; }эта карта коммутатора затем используется для создания индекса для
lookupswitchилиtableswitchинструкция JVM. Он преобразует каждое значение enum в соответствующий индекс от 1 до [количество случаев переключения].перечислитель.класс
public java.lang.String getName(EnumClass); Code: 0: getstatic #2; //Field EnumUser.$SwitchMap$EnumClass:[I 3: aload_1 4: invokevirtual #3; //Method EnumClass.ordinal:()I 7: iaload 8: lookupswitch{ //2 1: 36; 2: 39; default: 42 } 36: ldc #4; //String value 1 38: areturn 39: ldc #5; //String value 3 41: areturn 42: ldc #6; //String other 44: areturn
tableswitchиспользуется, если есть три или более случаев переключения, поскольку он выполняет более эффективный поиск постоянного времени противlookupswitchлинейный поиск. Технически говоря javac может опустить весь этот бизнес с картой синтетического коммутатора, когда он используетlookupswitch.предположения: у меня нет компилятора Eclipse для тестирования, но я предполагаю, что он не беспокоится о синтетическом классе и просто использует
lookupswitch. Или, возможно, для этого требуется больше случаев переключения, чем оригинальный asker, протестированный с помощью перед этим "угпрадес" доtableswitch.
на 1 $и т. д. файлы возникают при использовании функции" реализация метода для каждого экземпляра " перечислений Java, например:
public enum Foo{ YEA{ public void foo(){ return true }; }, NAY{ public void foo(){ return false }; }; public abstract boolean foo(); }выше будет создано три файла класса, один для базового класса перечисления и по одному для YEA и NAY для хранения различных реализаций foo().
на уровне байт-кода перечисления являются просто классами, и для того, чтобы каждый экземпляр перечисления реализовывал метод по-разному, для каждого из них должен быть другой класс например,
однако это не учитывает дополнительные файлы классов, созданные для пользователей перечисления, и я подозреваю, что это просто результат анонимных классов и не имеет ничего общего с перечислениями.
таким образом, чтобы избежать создания таких файлов дополнительных классов, не используйте реализации метода для каждого экземпляра. В случаях, подобных приведенным выше, когда методы возвращают константы, вместо этого можно использовать открытый конечный набор полей в конструкторе (или частное поле с открытым геттер, если вы предпочитаете). Если вам действительно нужны методы с разной логикой для разных экземпляров enum, то вы не можете избежать дополнительных классов, но я бы счел это довольно экзотической и редко необходимой функцией.
Я считаю, что это делается для предотвращения разрыва коммутаторов, если порядок перечисления изменяется, не перекомпилируя класс с помощью коммутатора. Рассмотрим следующий случай:
enum A{ ONE, //ordinal 0 TWO; //ordinal 1 } class B{ void foo(A a){ switch(a){ case ONE: System.out.println("One"); break; case TWO: System.out.println("Two"); break; } } }без карты коммутатора,
foo()примерно переводится как:void foo(A a){ switch(a.ordinal()){ case 0: //ONE.ordinal() System.out.println("One"); break; case 1: //TWO.ordinal() System.out.println("Two"); break; } }поскольку операторы Case должны быть константами времени компиляции (например, не вызовы методов). В этом случае, если заказ
Aвключается,foo()будет печатать "один" для двух, и наоборот.
в Java перечисления-это действительно просто классы с некоторым синтаксическим сахаром.
поэтому в любое время, когда вы определяете новое перечисление, компилятор Java создаст для вас соответствующий файл класса. (Независимо от того, насколько просто перечисление).
нет способа обойти это, кроме как не использовать перечисления.
Если пространство ограничено, вы всегда можете просто использовать константы.
насколько я знаю, учитывая перечисление с именем
Operationвы получите дополнительные файлы класса, исключая очевидныйOperation.class, и один на значение перечисления, если вы используетеabstract methodвот так:enum Operation { ADD { double op(double a, double b) { return a + b; } }, SUB { double op(double a, double b) { return a - b; } }; abstract double op(double a, double b); }
Comments