Что такое перечисления и чем они полезны?


488

Сегодня я просматривал некоторые вопросы на этом сайте и нашел упоминание об enum использовании в шаблоне синглтона о предполагаемых преимуществах безопасности потоков для такого решения.

Я никогда не использовал enums, и я программирую на Java уже более двух лет. И, видимо, они сильно изменились. Теперь они даже полностью поддерживают ООП внутри себя.

Теперь, почему и для чего мне использовать enum в повседневном программировании?



возможный дубликат Enum в Java. Преимущества?
finnw

Вы не можете создавать экземпляры перечислений, потому что у них есть закрытый конструктор, они создаются при запуске jvm, поэтому его синглтон, перечисления не являются поточно-ориентированными.
Арнав Джоши

Ответы:


633

Вы должны всегда использовать перечисления, когда переменная (особенно параметр метода) может взять только одно из небольшого набора возможных значений. Примерами могут быть такие вещи, как константы типа (статус контракта: «постоянный», «временный», «ученик») или флаги («выполнить сейчас», «отложить выполнение»).

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

Кстати, чрезмерное использование перечислений может означать, что ваши методы делают слишком много (часто лучше иметь несколько отдельных методов, а не один метод, который принимает несколько флагов, которые изменяют то, что он делает), но если вам нужно использовать флаги или коды типов, перечисления путь

Как пример, что лучше?

/** Counts number of foobangs.
 * @param type Type of foobangs to count. Can be 1=green foobangs,
 * 2=wrinkled foobangs, 3=sweet foobangs, 0=all types.
 * @return number of foobangs of type
 */
public int countFoobangs(int type)

против

/** Types of foobangs. */
public enum FB_TYPE {
 GREEN, WRINKLED, SWEET, 
 /** special type for all types combined */
 ALL;
}

/** Counts number of foobangs.
 * @param type Type of foobangs to count
 * @return number of foobangs of type
 */
public int countFoobangs(FB_TYPE type)

Вызов метода как:

int sweetFoobangCount = countFoobangs(3);

затем становится:

int sweetFoobangCount = countFoobangs(FB_TYPE.SWEET);

Во втором примере сразу ясно, какие типы разрешены, документы и реализация не могут быть синхронизированы, и компилятор может применить это. Кроме того, неверный звонок, как

int sweetFoobangCount = countFoobangs(99);

больше не возможно.


41
Если вы используете перечисления и хотите разрешить комбинацию значений, используйте EnumSet. Это идет со всеми видами аккуратных помощников, таких как public static final EnumSet <FB_TYPE> ALL = EnumSet.allOf (FB_TYPE.class);
RobAu

20
Эти ответы мне очень нравятся в SO, потому что кто-то приложил к этому реальные усилия, хорошую работу, я понимаю, перечисления сейчас!
посвящено

1
Я не думаю, что это правда you should *always* use enums when a variable can only take one out of a small set of possible values. Использование постоянных значений в качестве «двоичного флага» (с логическим orвводом) может быть полезно для приложений реального времени, где у вас мало памяти.
Elist

@ Использование списков enume повышает удобочитаемость ваших кодов. Если вы используете константы в какое-то время, вы или ваш преемник не знаете, почему они используются ...
:)

136

Зачем использовать какую-либо функцию языка программирования? Причина, по которой у нас есть языки,

  1. Программисты могут эффективно и правильно выражать алгоритмы в форме, которую могут использовать компьютеры.
  2. Сопровождающие понимают алгоритмы, написанные другими, и правильно вносят изменения.

Перечисления улучшают как вероятность правильности, так и читабельности без написания большого количества шаблонов. Если вы хотите написать шаблонный шаблон, вы можете «смоделировать» перечисления:

public class Color {
    private Color() {} // Prevent others from making colors.
    public static final Color RED = new Color();
    public static final Color AMBER = new Color();
    public static final Color GREEN = new Color();
}

Теперь вы можете написать:

Color trafficLightColor = Color.RED;

Приведенный выше шаблон имеет тот же эффект, что и

public enum Color { RED, AMBER, GREEN };

Оба предоставляют одинаковый уровень проверки справки от компилятора. Boilerplate просто больше печатать. Но сохранение большого количества печати делает программиста более эффективным (см. 1), так что это полезная функция.

Это стоит по крайней мере еще по одной причине:

Смена операторов

Одна вещь, которую static finalперечисленное выше моделирование перечисления не дает вам, это хорошие switchслучаи. Для перечислимых типов переключатель Java использует тип своей переменной, чтобы вывести область действия перечислений, поэтому для enum Colorвышесказанного вам просто нужно сказать:

Color color = ... ;
switch (color) {
    case RED:
        ...
        break;
}

Обратите внимание, что это не Color.REDв случаях. Если вы не используете enum, единственный способ использовать именованные величины с switchчем-то вроде:

public Class Color {
    public static final int RED = 0;
    public static final int AMBER = 1;
    public static final int GREEN = 2;
}

Но теперь переменная для хранения цвета должна иметь тип int. Хорошая проверка перечисления и static finalмоделирования компилятором исчезла. Не счастлив.

Компромиссом является использование скалярного члена в симуляции:

public class Color {
    public static final int RED_TAG = 1;
    public static final int AMBER_TAG = 2;
    public static final int GREEN_TAG = 3;

    public final int tag;

    private Color(int tag) { this.tag = tag; } 
    public static final Color RED = new Color(RED_TAG);
    public static final Color AMBER = new Color(AMBER_TAG);
    public static final Color GREEN = new Color(GREEN_TAG);
}

Сейчас же:

Color color = ... ;
switch (color.tag) {
    case Color.RED_TAG:
        ...
        break;
}

Но обратите внимание, еще более шаблонный!

Использование enum в качестве синглтона

Из вышеприведенного примера видно, почему перечисление предоставляет способ реализации синглтона. Вместо того чтобы писать:

public class SingletonClass {
    public static final void INSTANCE = new SingletonClass();
    private SingletonClass() {}

    // all the methods and instance data for the class here
}

а затем доступ к нему с

SingletonClass.INSTANCE

мы можем просто сказать

public enum SingletonClass {
    INSTANCE;

    // all the methods and instance data for the class here
}

что дает нам то же самое. Мы можем уйти с этим , потому что Java перечислений будут реализованы в виде полных классов только с небольшим синтаксическим сахаром посыпают сверху. Это опять-таки менее шаблонно, но не очевидно, если идиома вам не знакома. Мне также не нравится тот факт, что вы получаете различные функции перечисления, даже если они не имеют особого смысла для синглтона: ordи valuesт. Д. (На самом деле есть более Color extends Integerсложная симуляция, где это будет работать с switch, но это настолько сложно, что это даже больше ясно показывает, почему enumэто лучшая идея.)

Поток безопасности

Потоковая безопасность является потенциальной проблемой только тогда, когда синглтоны создаются лениво без блокировки.

public class SingletonClass {
    private static SingletonClass INSTANCE;
    private SingletonClass() {}
    public SingletonClass getInstance() {
        if (INSTANCE == null) INSTANCE = new SingletonClass();
        return INSTANCE;
    }

    // all the methods and instance data for the class here
}

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

Однако приведенный static finalвыше код не имеет этой проблемы. Он создает экземпляр с нетерпением во время загрузки класса. Загрузка классов синхронизирована.

enumОдноточечно эффективно ленивая , потому что он никогда не инициализируется до первого использования. Инициализация Java также синхронизирована, поэтому несколько потоков не могут инициализировать более одного экземпляра INSTANCE. Вы получаете лениво инициализированный синглтон с очень небольшим кодом. Единственный минус - довольно туманный синтаксис. Вам нужно знать идиому или полностью понимать, как работают загрузка и инициализация класса, чтобы знать, что происходит.


15
Последний абзац действительно прояснил мне ситуацию с синглтоном. Спасибо! Любой читатель, который скользит, должен перечитать это.
Василий Бурк

Я читал это, stackoverflow.com/questions/16771373/… . Теперь я запутался с вашим последним пунктом. Каким образом enum не предоставляет функцию инициализации lay?
Дипанкар Сингх

static finalполя инициализируются во время инициализации класса , а не во время загрузки. Это точно так же, как с enumпостоянной инициализацией (на самом деле они идентичны под капотом). Вот почему пытаться реализовать «умный» ленивый код инициализации для синглетонов всегда было бессмысленно, даже в первой версии Java.
Хольгер

42

Помимо уже упомянутых вариантов использования, я часто нахожу перечисления полезными для реализации шаблона стратегии, следуя некоторым основным рекомендациям ООП:

  1. Наличие кода, в котором находятся данные (то есть внутри самого перечисления - или часто внутри констант перечисления, которые могут переопределять методы).
  2. Реализация интерфейса (или более), чтобы не связывать клиентский код с перечислением (который должен обеспечивать только набор реализаций по умолчанию).

Простейшим примером будет набор Comparatorреализаций:

enum StringComparator implements Comparator<String> {
    NATURAL {
        @Override
        public int compare(String s1, String s2) {
            return s1.compareTo(s2);
        }
    },
    REVERSE {
        @Override
        public int compare(String s1, String s2) {
            return NATURAL.compare(s2, s1);
        }
    },
    LENGTH {
        @Override
        public int compare(String s1, String s2) {
            return new Integer(s1.length()).compareTo(s2.length());
        }
    };
}

Этот «шаблон» можно использовать в гораздо более сложных сценариях, широко используя все полезности, которые входят в enum: перебирая экземпляры, полагаясь на их неявный порядок, извлекая экземпляр по его имени, статические методы предоставляют правильный экземпляр для конкретного контекста и т. д. И все же у вас все это скрыто за интерфейсом, поэтому ваш код будет работать с пользовательскими реализациями без изменений на случай, если вы захотите что-то, что недоступно среди «опций по умолчанию».

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


2
Вы также можете добавить CASE_INSENSITIVE { @Override public int compare(String s1, String s2) { return s1.compareToIgnoreCase(s2); }. Еще одно преимущество, которое не упомянуто, заключается в том, что вы получаете надежную поддержку сериализации; постоянная форма содержит только имя класса и имя константы, независимо от деталей реализации ваших компараторов.
Хольгер

32

То, что ни один из других ответов не охватил, что делает перечисления особенно мощными, - это возможность иметь шаблонные методы . Методы могут быть частью базового перечисления и переопределяться каждым типом. И, благодаря поведению, прикрепленному к enum, оно часто устраняет необходимость в конструкциях if-else или операторах switch, как показано в этом сообщении в блоге, - где enum.method()выполняется то, что первоначально будет выполняться внутри условного выражения . В этом же примере показано использование статического импорта с перечислениями, а также создание более чистого кода, подобного DSL.

Некоторые другие интересные качества включают в себя тот факт, что перечисления обеспечивают реализацию equals(), toString()и hashCode()и реализуют Serializableи Comparable.

Для полного изложения всего, что может предложить перечисление, я настоятельно рекомендую Брюс Экель « Размышление в Java 4-е издание», в котором целая глава посвящена этой теме. Особенно яркими являются примеры игр «Камень, бумага, ножницы» (т.е. RoShamBo) в качестве перечислений.


23

Из документов Java -

Вы должны использовать типы enum каждый раз, когда вам нужно представить фиксированный набор констант. Это включает в себя естественные типы перечислений, такие как планеты в нашей солнечной системе и наборы данных, в которых вы знаете все возможные значения во время компиляции - например, выбор в меню, флаги командной строки и так далее.

Типичным примером является замена класса набором частных статических конечных констант типа int (в пределах разумного числа констант) типом enum. По сути, если вы думаете, что знаете все возможные значения «что-то» во время компиляции, вы можете представить это как тип enum. Перечисления обеспечивают читаемость и гибкость над классом с константами.

Несколько других преимуществ, которые я могу придумать для типов enum. Они всегда являются одним экземпляром определенного класса enum (отсюда и концепция использования enum по мере появления синглтона). Другое преимущество заключается в том, что вы можете использовать перечисления в качестве типа в выражении switch-case. Также вы можете использовать toString () в перечислении, чтобы напечатать их как читаемые строки.


8
Вы бы использовали enum для всех 366 дней в году? Это фиксированный набор (366) констант (дни не меняются). Так что это говорит о том, что вы должны. : - / Я бы ограничил размер фиксированного набора.
Мойнудин

2
@marcog Если немного подумать, можно было бы суммировать дни недели и дни каждого месяца в компактном перечислении.
Джеймс П.

1
Говоря о днях недели… DayOfWeekenum теперь предопределен, встроен в Java 8 и более поздние версии как часть инфраструктуры java.timeперенесен на Java 6 & 7 и на Android ).
Василий Бурк

19

Почему и зачем мне использовать enum в повседневном программировании?

Вы можете использовать Enum для представления небольшого фиксированного набора констант или режима внутреннего класса, увеличивая читабельность. Кроме того, Enums может обеспечить определенную жесткость при использовании в параметрах метода. Они предлагают интересную возможность передачи информации конструктору, как в примере Planets на сайте Oracle, и, как вы обнаружили, также позволяют простой способ создания одноэлементного шаблона.

пример: Locale.setDefault(Locale.US)читает лучше Locale.setDefault(1)и обеспечивает использование фиксированного набора значений, показанного в IDE, когда вы добавляете .разделитель вместо всех целых чисел.


@arnt Я тоже исправил грамматику в этом вопросе. Спасибо.
Джеймс П.

13

Enums перечисляют фиксированный набор значений в самодокументируемой форме.
Они делают ваш код более явным, а также менее подверженным ошибкам.

Почему бы не использовать Stringили intвместо Enumконстант?

  1. Компилятор не допустит опечаток и значений из фиксированного набора, поскольку перечисления сами являются типами . Последствия:
    • Вам не нужно будет писать предварительное условие (или руководство if), чтобы убедиться, что ваш аргумент находится в допустимом диапазоне.
    • Инвариант типа приходит бесплатно.
  2. Перечисления могут иметь поведение, как и любой другой класс.
  3. В Stringлюбом случае, вам, вероятно, понадобится такой же объем памяти, чтобы использовать s (это зависит от сложности Enum).

Более того, каждый из Enumэкземпляров является классом, для которого вы можете определить свое индивидуальное поведение.

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

Этот блог иллюстрирует некоторые из его приложений, таких как конечный автомат для синтаксического анализатора.


13

Полезно знать, что enumsтак же, как и другие классы с Constantполями иprivate constructor .

Например,

public enum Weekday
{
  MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
} 

Компилятор компилирует это следующим образом;

class Weekday extends Enum
{
  public static final Weekday MONDAY  = new Weekday( "MONDAY",   0 );
  public static final Weekday TUESDAY = new Weekday( "TUESDAY ", 1 );
  public static final Weekday WEDNESDAY= new Weekday( "WEDNESDAY", 2 );
  public static final Weekday THURSDAY= new Weekday( "THURSDAY", 3 );
  public static final Weekday FRIDAY= new Weekday( "FRIDAY", 4 );
  public static final Weekday SATURDAY= new Weekday( "SATURDAY", 5 );
  public static final Weekday SUNDAY= new Weekday( "SUNDAY", 6 );

  private Weekday( String s, int i )
  {
    super( s, i );
  }

  // other methods...
}

12

enumозначает перечисление, то есть упоминание (несколько вещей) один за другим.

Перечисление представляет собой тип данных , который содержит фиксированный набор констант.

ИЛИ

An enumпохож на a class, с фиксированным набором экземпляров, известных во время компиляции.

Например:

public class EnumExample {
    interface SeasonInt {
        String seasonDuration();
    }

    private enum Season implements SeasonInt {
        // except the enum constants remaining code looks same as class
        // enum constants are implicitly public static final we have used all caps to specify them like Constants in Java
        WINTER(88, "DEC - FEB"), SPRING(92, "MAR - JUN"), SUMMER(91, "JUN - AUG"), FALL(90, "SEP - NOV");

        private int days;
        private String months;

        Season(int days, String months) { // note: constructor is by default private 
            this.days = days;
            this.months = months;
        }

        @Override
        public String seasonDuration() {
            return this+" -> "+this.days + "days,   " + this.months+" months";
        }

    }
    public static void main(String[] args) {
        System.out.println(Season.SPRING.seasonDuration());
        for (Season season : Season.values()){
            System.out.println(season.seasonDuration());
        }

    }
}

Преимущества enum:

  • enum повышает безопасность типов при проверке во время компиляции, чтобы избежать ошибок во время выполнения.
  • enum может быть легко использован в switch
  • enum может быть пройден
  • enum может иметь поля, конструкторы и методы
  • enum может реализовывать много интерфейсов, но не может расширять любой класс, потому что он внутренне расширяет класс Enum

для большего


9

Что такое перечисление

  • enum - это ключевое слово, определенное для перечисления нового типа данных. Типизированные перечисления должны использоваться свободно. В частности, они представляют собой надежную альтернативу простым константам String или int, используемым в гораздо более старых API для представления наборов связанных элементов.

Зачем использовать enum

  • перечисления неявно являются окончательными подклассами java.lang.Enum
  • если перечисление является членом класса, оно неявно статично
  • new никогда не может использоваться с перечислением, даже внутри самого перечисления
  • name и valueOf просто используют текст констант enum, тогда как toString может быть переопределен для предоставления любого содержимого, если это необходимо
  • для констант перечисления равно и == равно одному и тому же и может использоваться взаимозаменяемо
  • константы enum неявно являются публичными static final

Запись

  • Перечисления не могут расширять любой класс.
  • Перечисление не может быть суперклассом.
  • порядок появления констант enum называется их «естественным порядком» и определяет порядок, используемый другими элементами: CompareTo, порядок итераций значений, EnumSet, EnumSet.range.
  • Перечисление может иметь конструкторы, статические блоки и блоки экземпляров, переменные и методы, но не может иметь абстрактных методов.

4
Вы не объясняете, почему использовать перечисления. Вы просто перечислите несколько свойств перечислений под несвязанными заголовками.
Дункан Джонс

@DuncanJones разве термины final, static дают цель использования перечислений?
tinker_fairy

8

Помимо всего сказанного другими. В более старом проекте, над которым я работал, во многих отношениях между сущностями (независимыми приложениями) использовались целые числа, которые представляли собой небольшой набор. Было полезно объявить набор какenum статические методы для получения enumобъекта valueи наоборот. Код выглядел чище, переключал удобство использования и облегчал запись в журналы.

enum ProtocolType {
    TCP_IP (1, "Transmission Control Protocol"), 
    IP (2, "Internet Protocol"), 
    UDP (3, "User Datagram Protocol");

    public int code;
    public String name;

    private ProtocolType(int code, String name) {
        this.code = code;
        this.name = name;
    }

    public static ProtocolType fromInt(int code) {
    switch(code) {
    case 1:
        return TCP_IP;
    case 2:
        return IP;
    case 3:
        return UDP;
    }

    // we had some exception handling for this
    // as the contract for these was between 2 independent applications
    // liable to change between versions (mostly adding new stuff)
    // but keeping it simple here.
    return null;
    }
}

Создать enumобъект из полученных значений (например, 1,2), используяProtocolType.fromInt(2) Запись в журналы, используяmyEnumObj.name

Надеюсь это поможет.


6

Enum наследует все методы Objectкласса и абстрактного классаEnum . Таким образом, вы можете использовать его методы для отражения, многопоточности, сериализации, сопоставления и т. Д. Если вы просто объявите статическую константу вместо Enum, вы не сможете. Кроме того, значение Enum может быть передано и на уровень DAO.

Вот пример программы для демонстрации.

public enum State {

    Start("1"),
    Wait("1"),
    Notify("2"),
    NotifyAll("3"),
    Run("4"),
    SystemInatilize("5"),
    VendorInatilize("6"),
    test,
    FrameworkInatilize("7");

    public static State getState(String value) {
        return State.Wait;
    }

    private String value;
    State test;

    private State(String value) {
        this.value = value;
    }

    private State() {
    }

    public String getValue() {
        return value;
    }

    public void setCurrentState(State currentState) {
        test = currentState;
    }

    public boolean isNotify() {
        return this.equals(Notify);
    }
}

public class EnumTest {

    State test;

    public void setCurrentState(State currentState) {
        test = currentState;
    }

    public State getCurrentState() {
        return test;
    }

    public static void main(String[] args) {
        System.out.println(State.test);
        System.out.println(State.FrameworkInatilize);
        EnumTest test=new EnumTest();
        test.setCurrentState(State.Notify);
        test. stateSwitch();
    }

    public void stateSwitch() {
        switch (getCurrentState()) {
        case Notify:
            System.out.println("Notify");
            System.out.println(test.isNotify());
            break;
        default:
            break;
        }
    }
}

4

ENum расшифровывается как «Enumerated Type». Это тип данных, имеющий фиксированный набор констант, которые вы определяете сами.


Вы не объяснили, почему использовать перечисление.
Дункан Джонс

4
Да, я сделал. Вы используете его для хранения фиксированного набора констант, которые вы определяете сами. Конкретное применение этого зависит от вас. Нет такой вещи, как говорить, когда, почему или как что-то использовать. Это зависит от потребностей разработчика. Все, что вам нужно понять, это то, что это такое и как это работает.
Стив

4

На мой взгляд, все ответы, которые вы получили до сих пор, действительны, но по своему опыту я бы выразил это в нескольких словах:

Используйте перечисления, если вы хотите, чтобы компилятор проверил правильность значения идентификатора.

В противном случае вы можете использовать строки, как вы всегда это делали (возможно, вы определили некоторые «соглашения» для своего приложения), и вы будете очень гибкими ... но вы не получите 100% -ную защиту от опечаток в ваших строках, и вы сможете их реализовать только во время выполнения.


3

Используйте перечисления для БЕЗОПАСНОСТИ ТИПА, это языковая функция, поэтому вы обычно получаете:

  • Поддержка компилятора (сразу смотрите проблемы с типом)
  • Поддержка инструментов в IDE (автозаполнение в случае переключения, пропущенные случаи, принудительное выполнение по умолчанию, ...)
  • В некоторых случаях производительность enum также велика ( EnumSet , безопасная альтернатива традиционным основанным на int «битовым флагам» ).

У перечислений могут быть методы, конструкторы, вы даже можете использовать перечисления внутри перечислений и комбинировать перечисления с интерфейсами.

Представьте перечисления как типы для замены четко определенного набора констант int (которые Java «унаследовала» от C / C ++) и в некоторых случаях для замены битовых флагов.

Книга Effective Java 2nd Edition содержит целую главу о них и более подробно. Также см. Этот пост переполнения стека .


2

Java позволяет ограничить переменную одним из нескольких предопределенных значений, другими словами, одним значением из перечисленного списка. Использование enumsможет помочь уменьшить количество ошибок в вашем коде. Вот пример enumsвне класса:

enums coffeesize{BIG , HUGE , OVERWHELMING }; 
//This semicolon is optional.

Это ограничивает coffeesizeк иметь либо: BIG, HUGEили в OVERWHELMINGкачестве переменной.


2

Enum? Зачем его использовать? Я думаю, что это более понятно, когда вы будете его использовать. У меня такой же опыт.

Скажем, у вас есть операция создания, удаления, редактирования и чтения базы данных.

Теперь, если вы создаете enum как операцию:

public enum operation {
    create("1")
    delete("2")
    edit("3")
    read("4")

    // You may have is methods here
    public boolean isCreate() {
        return this.equals(create);
    }
    // More methods like the above can be written

}

Теперь вы можете объявить что-то вроде:

private operation currentOperation;

// And assign the value for it 
currentOperation = operation.create

Таким образом, вы можете использовать его по-разному. Всегда хорошо иметь enum для конкретных вещей, так как работа с базой данных в приведенном выше примере может контролироваться проверкой currentOperation . Возможно, можно сказать, что это можно сделать с помощью переменных и целочисленных значений. Но я считаю, что Enum - более безопасный и программистский путь.

Другое дело: я думаю, что каждый программист любит логическое , не так ли? Потому что он может хранить только два значения, два конкретных значения. Таким образом, можно думать, что Enum имеет такой же тип средств, где пользователь будет определять, сколько и какой тип значения он будет хранить, просто немного по-другому. :)


1

До сих пор мне никогда не приходилось использовать перечисления. Я читал о них с тех пор, как они были введены в версии 1.5 или версии тигра, как это было отозвано в тот же день. Они никогда не решали «проблему» для меня. Для тех, кто использует это (и я вижу, что многие из них делают), я уверен, что это определенно служит какой-то цели. Просто мои 2 фунта.


1

Здесь есть много ответов, просто хочу указать два конкретных:

1) Использование в качестве констант в Switch-caseутверждении. Переключатель case не позволит вам использовать объекты String для case. Перечисления пригодятся. Больше: http://www.javabeat.net/2009/02/how-to-use-enum-in-switch/

2) Реализация Singleton Design Pattern- Enum снова приходит на помощь. Использование здесь: Каков наилучший подход для использования Enum в качестве синглтона в Java?


1

То, что дало мне момент ах-ха, это осознание: у Enum есть частный конструктор, доступный только через публичное перечисление:

enum RGB {
    RED("Red"), GREEN("Green"), BLUE("Blue");

    public static final String PREFIX = "color ";

    public String getRGBString() {
        return PREFIX + color;
    }

    String color;

    RGB(String color) {
        this.color = color;
    }
}

public class HelloWorld {
    public static void main(String[] args) {
        String c = RGB.RED.getRGBString();
        System.out.print("Hello " + c);
    }
}

1

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

public enum Items {
    MESSAGES, CHATS, CITY_ONLINE, FRIENDS, PROFILE, SETTINGS, PEOPLE_SEARCH, CREATE_CHAT
}

@Override
public boolean onCreateOptionsMenu(Menu menuPrm) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menuPrm);
    View itemChooserLcl;
    for (int i = 0; i < menuPrm.size(); i++) {
        MenuItem itemLcl  = menuPrm.getItem(i);
            itemChooserLcl = itemLcl.getActionView();
            if (itemChooserLcl != null) {
                 //here Im marking each View' tag by enume values:
                itemChooserLcl.setTag(Items.values()[i]);
                itemChooserLcl.setOnClickListener(drawerMenuListener);
            }
        }
    return true;
}
private View.OnClickListener drawerMenuListener=new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Items tagLcl= (Items) v.getTag();
        switch (tagLcl){
            case MESSAGES: ;
            break;
            case CHATS : ;
            break;
            case CITY_ONLINE : ;
            break;
            case FRIENDS : ;
            break;
            case  PROFILE: ;
            break;
            case  SETTINGS: ;
            break;
            case  PEOPLE_SEARCH: ;
            break;
            case  CREATE_CHAT: ;
            break;
        }
    }
};

1

По своему опыту я видел, что использование Enum иногда приводит к тому, что системы очень трудно изменить. Если вы используете Enum для набора доменных значений, которые часто меняются, и у него есть много других классов и компонентов, которые зависят от него, вы можете подумать не использовать Enum.

Например, торговая система, которая использует Enum для рынков / бирж. Существует множество рынков, и почти наверняка будет много подсистем, которым необходим доступ к этому списку рынков. Каждый раз, когда вы хотите, чтобы новый рынок был добавлен в вашу систему, или если вы хотите удалить рынок, возможно, что все под солнцем придется перестраивать и выпускать.

Лучшим примером будет что-то вроде типа категории продукта. Допустим, ваше программное обеспечение управляет запасами для универмага. Существует много категорий товаров и множество причин, по которым этот список категорий может измениться. Менеджеры могут захотеть снабдить новой линейкой продуктов, избавиться от других линий продуктов и, возможно, время от времени реорганизовывать категории. Если вам нужно перестроить и заново развернуть все свои системы просто потому, что пользователи хотят добавить категорию продукта, то вы взяли что-то, что должно быть простым и быстрым (добавление категории), и сделали это очень трудным и медленным.

В итоге, перечисления хороши, если данные, которые вы представляете, очень статичны во времени и имеют ограниченное количество зависимостей. Но если данные сильно меняются и имеют много зависимостей, вам нужно что-то динамическое, что не проверяется во время компиляции (например, таблица базы данных).


1

Синглтон на основе перечисления

современный взгляд на старую проблему

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

public enum Singleton {
    SINGLETON; 
    public void method() { }
}

Как это работает? Ну, вторая строка кода может рассматриваться примерно так:

public final static Singleton SINGLETON = new Singleton(); 

И мы получаем старый добрый ранний инициализированный синглтон.

Помните, что, поскольку это enum, вы всегда можете получить доступ к экземпляру через Singleton.INSTANCE:

Singleton s = Singleton.INSTANCE;
преимущества
  • Чтобы предотвратить создание других экземпляров синглтона во время десериализации, используйте синглтон на основе перечисления, потому что JVM заботится о сериализации перечисления. Сериализация и десериализация перечислений работают иначе, чем для обычных объектов Java. Единственное, что сериализуется, это имя значения перечисления. В процессе десериализации enumvalueOf метод с десериализованным именем для получения желаемого экземпляра.
  • Enum на основе синглтона позволяет защитить себя от отраженной атаки. Тип enum фактически расширяет Enumкласс java . Причина, по которой отражение не может быть использовано для создания экземпляров объектов типа enum, заключается в том, что спецификация java запрещена, и это правило закодировано в реализации newInstanceметода Constructorкласса, который обычно используется для создания объектов посредством отражения:
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
    throw new IllegalArgumentException("Cannot reflectively create enum objects");
  • Enum не должен быть клонирован, потому что должен быть ровно один экземпляр каждого значения.
  • Самый лаконичный код среди всей синглтон-реализации.
Недостатки
  • Синглтон на основе перечисления не допускает отложенной инициализации.
  • Если вы изменили свой дизайн и хотели преобразовать свой синглтон в мультитон, enum не допустит этого. Многотонный шаблон используется для контролируемого создания нескольких экземпляров, которыми он управляет с помощью a map. Вместо того, чтобы иметь по одному экземпляру на приложение (например, java.lang.Runtime), многотонный шаблон обеспечивает один экземпляр на ключ .
  • Enum появляется только в Java 5, поэтому вы не можете использовать его в предыдущей версии.

Существует несколько реализаций синглтон-паттернов, каждый из которых имеет свои преимущества и недостатки.

  • Стремительная загрузка синглтона
  • Двойная проверка блокировки синглтона
  • Идиома владельца инициализации по требованию
  • Синглтон на основе перечисления

Подробное описание каждого из них слишком многословно, поэтому я просто поместил ссылку на хорошую статью - Все, что вы хотите знать о Синглтоне


0

Я бы использовал перечисления в качестве полезного инструмента отображения, избегая множественных if-else при условии, что некоторые методы реализованы.

public enum Mapping {

    ONE("1"),
    TWO("2");

    private String label;

    private Mapping(String label){
        this.label = label;
    }

    public static Mapping by(String label) {

        for(Mapping m: values() {
            if(m.label.equals(label)) return m;
        }

        return null;
    }

}

Так что метод by(String label) позволяет вам получить значение Enumerated неперечисленным. Кроме того, можно изобрести отображение между 2 перечислениями. Можно также попробовать «1 ко многим» или «многие ко многим» в дополнение к «одно к одному» по умолчанию отношение

В конце концов, enumэто класс Java. Таким образом, у вас может быть mainметод внутри него, который может быть полезен, когда нужно выполнить некоторые операции отображения argsсразу.


0

Вместо того чтобы делать кучу объявлений const int

Вы можете сгруппировать их все в 1 enum

Так что все это организовано общей группой, к которой они принадлежат


0

Перечисления похожи на классы. Как и класс, он также имеет методы и атрибуты.

Различия с классом: 1. константы перечисления являются общедоступными, статическими, конечными. 2. перечисление не может использоваться для создания объекта и не может расширять другие классы. Но он может реализовать интерфейсы.


0

В дополнение к @BradB Ответ:

Это так верно ... Странно, что это единственный ответ, который упоминает об этом. Когда новички обнаруживают перечисления, они быстро воспринимают это как фокус для проверки правильности идентификатора компилятора. И когда код предназначен для использования в распределенных системах, они плачут ... через месяц. Поддерживать обратную совместимость с перечислениями, которые содержат нестатический список значений, - реальная проблема, и боль. Это связано с тем, что когда вы добавляете значение в существующее перечисление, его тип изменяется (несмотря на то, что имя не меняется).

«Хо, подожди, это может выглядеть как один и тот же тип, верно? В конце концов, они перечисления с одним и тем же именем - и не являются ли перечисления просто целыми числами под капотом?» И по этим причинам ваш компилятор, скорее всего, не будет помечать использование одного определения самого типа там, где ожидалось другое. Но на самом деле они (в наиболее важных отношениях) разные типы. Самое главное, они имеют разные домены данных - значения, которые являются приемлемыми для данного типа. Добавляя значение, мы эффективно изменили тип перечисления и поэтому нарушаем обратную совместимость.

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.