Enum-Всемогущий

в 16:20, , рубрики: enum, Enum-Всемогущий, java, singleton

Вводная

Очень часто, мы используем инструменты строго по назначению, запрещая себе делать шаг влево или вправо. Но что если мы немного 'забудемся'? Что, если мы посмотрим на привычные нам вещи под другим углом? В этой статье собраны подходы использования перечислений и проведен небольшой эксперимент над ними. Сарказм, юмор и немного филосовских вопросов. Кому интересно, добро пожаловать под кат.

Предостережение

В этой статье вы не найдете для себя новых технических знаний, откровений, а если Вы жаждете их — то смело переходите к следующей статье. Тем не менее, здесь есть чему удивляться и есть над чем подумать. Технический юмор и филосовские мысли между строк.

Не рассказанная история...

Однажды попадает разработчик в место, где решают судьбу. Время спустя, перед ним появляется образ и спрашивает:
— Кто ты?
— Я, разработчик, звать Иван, — а про себя: Во встрял то.
Голос опять:
— Хочешь туда?. Взгляд на дверь, за которой рай.
— Ага, — робко Иван.
— Чего поведаешь мне?, — спрашивает Голос.
Немного подумав, Иван начинает говорить:
— Есть в 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?

public enum Highlander {
    JAVA;
    Highlander() {
        if (ordinal() != 0) {
            throw new IllegalStateException("В живых должен остаться только один");
        }
        init();
    }
    private void init() {
        // код здесь!
    }
    /** java-doc */
    public String getOwner(String index) {
        // логика здесь
        return "Джеймс Гослинг, Sun Microsystems";
    }
}

Ты за Scala?

public enum Highlander {
    SCALA {};
    Highlander() {
        if (ordinal() != 0) {
            throw new IllegalStateException("В живых должен остаться только один");
        }
        init();
    }
    private void init() {
        // код здесь!
    }
    /** java-doc */
    public String getOwner(String index) {
        // логика здесь
        return "Мартин Одерски, Федеральная политехническая школа Лозанны";
    }
}

Ты за Kotlin?
public enum Highlander {
    KOTLIN {};
    Highlander() {
        if (ordinal() != 0) {
            throw new IllegalStateException("В живых должен остаться только один");
        }
        init();
    }
    private void init() {
        // код здесь!
    }
    /** java-doc */
    public String getOwner(String index) {
        // логика здесь
        return "Андрей Бреслав, JetBrains";
    }
}

— Джошуа Блох говорит*, что это лучшая реализация Синглтона.
— Ну а ты?
— А что я? Это, это — это синглтон-фабрика, для хранения одного единственного элемента, тить колотить...

Highlander.valueOf("JAVA");

Это точка для доступа к массиву для хранения одного единственного элемента, тить колотить...

Highlander.values()[0];

Это наследник класса ..., — хотел было продолжить Иван, но был приятно удивлен:
— Проходи...

Немного выводов

Итого получается, что enum можно наделить следующими качествами и свойствами, в зависимости от точки обзора:

  • Перечисление и данные
  • Каркас для утилитного класса
  • Каркас для синглтона класса + антипатерн прилагается
  • Каркас для фабрики

Эксперимент

Я решил понять, сколько можно максимально сгенерировать элементов перечислений. Мой собственный ответ и реальность настолько разошлись, что я усомнился в своих знаниях. Прежде чем Вы посмотрите ниже, попытайтесь дать ответ самостоятельно. Упростим, скажите хотя бы порядок? Вот код, который я использовал для генерации класса перечислений (на быструю руку):

Код-генерации 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 элементов перечислений. А дальше вот это:

Ошибка_3

Total 5000
[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 этажа, сначала я получил такую ошибку:

Ошибка_1

Total 134217727
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

А потом, немного подвернув ее, такую:

Ошибка_2

Total 8388607
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

Автор: reforms

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js