Вводная
Очень часто, мы используем инструменты строго по назначению, запрещая себе делать шаг влево или вправо. Но что если мы немного 'забудемся'? Что, если мы посмотрим на привычные нам вещи под другим углом? В этой статье собраны подходы использования перечислений и проведен небольшой эксперимент над ними. Сарказм, юмор и немного филосовских вопросов. Кому интересно, добро пожаловать под кат.
Предостережение
В этой статье вы не найдете для себя новых технических знаний, откровений, а если Вы жаждете их — то смело переходите к следующей статье. Тем не менее, здесь есть чему удивляться и есть над чем подумать. Технический юмор и филосовские мысли между строк.
Не рассказанная история...
Однажды попадает разработчик в место, где решают судьбу. Время спустя, перед ним появляется образ и спрашивает:
— Кто ты?
— Я, разработчик, звать Иван, — а про себя: Во встрял то.
Голос опять:
— Хочешь туда?. Взгляд на дверь, за которой рай.
— Ага, — робко Иван.
— Чего поведаешь мне?, — спрашивает Голос.
Немного подумав, Иван начинает говорить:
— Есть в java Enum-Всемогущий.
— Как так, Всемогущий? — перебивает Голос с возмущением. — Это только перечисление!
public enum JavaLanguage {
JAVA("Forever"),
SCALA("Next generation") {},
KOTLIN("Future") {};
private final String claim;
JavaLanguage(String claim) {
this.claim = claim;
}
public String getClaim() {
return claim;
}
}
— Ага, — отвечает разраб, Но не только.
— Докажи!
— Enum как гвозди, утильным могёт.
public enum LanguageUtils {
;
/** java-doc */
LanguageUtils() {
throw new IllegalStateException("Это не перечисление");
}
/** java-doc */
public static String[] getKeyWords(String languageName) {
if (languageName != null) {
// немного логики здесь
return loadFromResource(languageName + "/keywords.dat");
}
throw new IllegalStateException("Необходимо указать язык");
}
/** java-doc */
private static synchronized String[] loadFromResource(String resourceName) {
String[] items = null;
// Код загрузки здесь
return items;
}
/** Много статических методов здесь */
}
— Во так чудеса, но… Наследника у него нет!
— А это как посмотреть. А кого считать Наследником? Scala? Kotlin?
— Давай пример, не дожидаясь пока разраб завершит свой вопрос
// в JavaLanguage.java файле
public static void main(String[] args) {
// it's true
if (JAVA.getClass() == SCALA.getClass().getSuperclass()) {
System.out.println("Наследник то есть!");
}
// it's true
if (JAVA.getClass() == KOTLIN.getClass().getSuperclass()) {
System.out.println("Да не один!");
}
}
— Да уж, интересные Вы ребята, прогеры — уже улыбаясь, говорит Голос, — Но малова-то будет
Почесав репу, Иван продолжил:
— Enum-то у нас фабрика!
— Не, было уже.
Пришлось, Ивану последний козырь достать:
— Enum-Синглтон, точно!
Выбери свое |
Ты за Java?
|
Ты за Scala?
|
Ты за Kotlin?
|
— Джошуа Блох говорит*, что это лучшая реализация Синглтона.
— Ну а ты?
— А что я? Это, это — это синглтон-фабрика, для хранения одного единственного элемента, тить колотить...
Highlander.valueOf("JAVA");
Это точка для доступа к массиву для хранения одного единственного элемента, тить колотить...Highlander.values()[0];
Это наследник класса ..., — хотел было продолжить Иван, но был приятно удивлен:
— Проходи...
Немного выводов
Итого получается, что enum можно наделить следующими качествами и свойствами, в зависимости от точки обзора:
- Перечисление и данные
- Каркас для утилитного класса
- Каркас для синглтона класса + антипатерн прилагается
- Каркас для фабрики
Эксперимент
Я решил понять, сколько можно максимально сгенерировать элементов перечислений. Мой собственный ответ и реальность настолько разошлись, что я усомнился в своих знаниях. Прежде чем Вы посмотрите ниже, попытайтесь дать ответ самостоятельно. Упростим, скажите хотя бы порядок? Вот код, который я использовал для генерации класса перечислений (на быструю руку):
package com.enums;
import java.io.*;
public class EnumGeneratorShort implements Closeable {
private BufferedWriter writer;
public EnumGeneratorShort(File outputFile) throws IOException {
writer = new BufferedWriter(new FileWriter(outputFile));
}
void append(String line) throws IOException {
writer.append(line);
}
void appendLine(String line) throws IOException {
writer.append(line).append("\n");
}
@Override
public void close() throws IOException {
if (writer != null) {
writer.close();
}
}
/** Сколько сгенерировать enum*/
private static final int ENUM_COUNT = XXXX; // где XXXX - размер
private static final char[] ALPHABET = new char[] {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c',
'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'};
public static void main(String[] args) throws IOException {
System.out.println("Start generation");
File outputFile = new File("src/main/java/com/enums/BigEnumShort.java");
try (EnumGeneratorShort enumGen = new EnumGeneratorShort(outputFile)) {
enumGen.appendLine("package com.enums;");
enumGen.appendLine("");
enumGen.appendLine("public enum BigEnumShort {");
enumGen.append("A");
int index = 1;
for (; index < ENUM_COUNT; index++) {
enumGen.appendLine(",");
String name = getName(index, "");
if ("if".equals(name) || "do".equals(name)) {
name = getName(++index, "");
}
enumGen.append(name);
}
enumGen.appendLine(";");
enumGen.appendLine("");
enumGen.appendLine(" public static void main(String[] args) {");
enumGen.appendLine(" System.out.println(\"Find enum \" + BigEnumShort.valueOf(\"B\"));");
enumGen.appendLine(" }");
enumGen.appendLine("}");
System.out.println("End generation. Total " + index);
}
}
public static String getName(int index, String before) {
if (index < ALPHABET.length) {
return before + ALPHABET[index];
}
int tail = index / ALPHABET.length;
int current = index % ALPHABET.length;
return getName(tail, before + ALPHABET[current]);
}
}
Вы уже предположили? Так вот, на семерочке мне удалось сгенерировать всего 2746 элементов перечислений. А дальше вот это:
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:compile (default-compile) on project bigenum: Compilation failure
[ERROR] /home/XXX/temp/BigEnum/bigenum/src/main/java/com/enums/BigEnumShort.java:[4,1] code too large
Но, так как я раскатал губу в 4 этажа, сначала я получил такую ошибку:
Compiling 1 source file to /home/XXX/temp/BigEnum/bigenum/target/classes
An exception has occurred in the compiler (1.7.0_51). Please file a bug at the Java Developer Connection (http://java.sun.com/webapps/bugreport) after checking the Bug Parade for duplicates. Include your program and the following diagnostic in your report. Thank you.
java.lang.IllegalArgumentException
А потом, немного подвернув ее, такую:
ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:compile (default-compile) on project bigenum: Compilation failure: Compilation failure:
[ERROR] /home/XXX/temp/BigEnum/bigenum/src/main/java/com/enums/BigEnumShort.java:[4,1] code too large
[ERROR] /home/XXX/temp/BigEnum/bigenum/src/main/java/com/enums/BigEnumShort.java:[3,8] too many constants
Еще мне было интересно, как такое перечисление смогут переварить известные декомпиляторы. Итого по субъективной оценке ожидания:
Место | Декомпилятор | Результат |
1 | fernflower.jar | ОК |
2 | jad | OK |
3 | procyon | Дождался |
4 | cfr_0_115.jar | Не дождался |
Спасибо вам за внимание.
Источники и вдохновители
Effective Java, 2nd Edition, by Joshua Bloch*
Википедия
Правильный Singleton в Java
Комментарий от apanasevich
Комментарий от Sirikid
Комментарии (17)
rPman
07.02.2017 00:05+1Я в шоке! 8 миллионов констант в одном файле, и компилятор его съел.
Как долго вообще шла компиляция? Меняется ли заметно скорость компиляции при использовании такого большого класса?reforms
07.02.2017 11:08+1Комп: 4 ядра, i5-2500 CPU @ 3.30GHz, оперативка 16G
Исходник: 85 мегабайт
Время компиляции: Total time: 05:04 min
sleeply4cat
07.02.2017 17:08А кто-нибудь может объяснить, почему в методе может быть u4 команд, но таблица исключений покрывает только u2, что, по-видимому, и является ограничением его размера?
apanasevich
08.02.2017 16:03А я еще я в предыдущем вашем посте написал вот такой коммент коммент.
И судя по ссылке оттуда выходит, что не нужно бросать исключения в конструкторе энума. Это лишний код. На который вам нужно писать лишние тесты (по-хорошему).
Наглядно написанное ораклом ссылку демонстрирует вот такой пример:
import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public final class Test { public static void main(String[] args) { try { final Constructor<?> qwertyConstructor = Qwerty.class.getDeclaredConstructors()[0]; qwertyConstructor.setAccessible(true); System.out.println("I've got " + qwertyConstructor.newInstance()); final Constructor<?> timeUnitConstructor = java.util.concurrent.TimeUnit.class.getDeclaredConstructors()[0]; timeUnitConstructor.setAccessible(true); timeUnitConstructor.newInstance(); System.out.println("You'll never see me."); } catch (IllegalAccessException | InvocationTargetException | IllegalArgumentException | InstantiationException x) { x.printStackTrace(); } } } class Qwerty { private Qwerty() { } }
I've got ololo.Qwerty@45ee12a7 java.lang.IllegalArgumentException: Cannot reflectively create enum objects at java.lang.reflect.Constructor.newInstance(Constructor.java:417) at ololo.Test.main(Test.java:15) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)
reforms
08.02.2017 16:20И судя по ссылке оттуда выходит, что не нужно бросать исключения в конструкторе энума. Это лишний код.
В этом тоже определенный сарказм, до точки с запятой в LanguageUtils, неожиданно:
SHAPITO_CIRCUS;
Ясно, что это апофеоз тупости, но все же, конструтор-смотрелка, все сделает как надо :)apanasevich
08.02.2017 16:23Я не понимаю, о чем вы. Где SHAPITO_CIRCUS?
reforms
08.02.2017 16:38Добавить разумеется в класс LanguageUtils, если отсутствует такой конструктор-старожил — плевое дело, если Вы понимаете о чем я :)
apanasevich
08.02.2017 16:45Удалить такой конструктор тоже плевое дело.
reforms
08.02.2017 16:48Хотите живой пример?
apanasevich
08.02.2017 16:49конечно
reforms
08.02.2017 16:56public enum LanguageUtils { SYNCH_OBJECT; /** java-doc */ public static void doWork1() { // много код здесь, а потом synchronized (SYNCH_OBJECT) { // doSomeReadWriteOperation1 - внутри изменение/чтение общего объект N1 } } /** java-doc */ public static String doWork2(String name) { // много код здесь, а потом synchronized (SYNCH_OBJECT) { // doSomeReadWriteOperation2 - внутри изменение/чтение общего объект N1 } return ""; } }
abbath0767
Относительно очевидные вещи, но интересно преподнесенные) спасибо за статью, хоть сейчас и не пятница, но формат близок
reforms
Благодарю