В чем преимущество переименования Java в сравнении с classом с публичными статическими конечными полями?

Я очень хорошо знаком с C #, но начал работать больше на Java. Я ожидал узнать, что enums на Java в основном эквивалентны enumsм на C #, но, видимо, это не так. Первоначально я был рад узнать, что enums Java могут содержать несколько fragmentов данных, которые кажутся очень выгодными ( http://docs.oracle.com/javase/tutorial/java/javaOO/enum.html ). Однако с тех пор я обнаружил много недостающих функций, которые тривиальны в C #, такие как возможность легко назначить элементу enums определенное значение и, следовательно, способность конвертировать целое число в перечисление без приличного количества усилий ( т.е. преобразовать целочисленное значение в соответствие с Java Enum ).

Поэтому мой вопрос заключается в следующем: есть ли какая-либо польза для перечислений Java над classом с кучей открытых статических конечных полей? Или это просто обеспечивает более компактный синтаксис?

EDIT: Позвольте мне быть более ясным. В чем преимущество перечислений Java над classом с кучей открытых статических конечных полей того же типа ? Например, в примере с планетами по первому каналу, в чем преимущество переименования над classом с этими общедоступными константами:

public static final Planet MERCURY = new Planet(3.303e+23, 2.4397e6); public static final Planet VENUS = new Planet(4.869e+24, 6.0518e6); public static final Planet EARTH = new Planet(5.976e+24, 6.37814e6); public static final Planet MARS = new Planet(6.421e+23, 3.3972e6); public static final Planet JUPITER = new Planet(1.9e+27, 7.1492e7); public static final Planet SATURN = new Planet(5.688e+26, 6.0268e7); public static final Planet URANUS = new Planet(8.686e+25, 2.5559e7); public static final Planet NEPTUNE = new Planet(1.024e+26, 2.4746e7); 

Насколько я могу судить, ответ Касабланки – единственный, который удовлетворяет этому.

Технически можно было действительно рассматривать enums как class с кучей типизированных констант, и это на самом деле то, как константы континуума реализованы внутри. Однако использование enum предоставляет полезные методы ( Enum javadoc ), которые вам в противном случае пришлось бы реализовать самостоятельно, например Enum.valueOf .

  1. Введите безопасность и безопасность.
  2. Гарантированный синглтон.
  3. Возможность определять и переопределять методы.
  4. Возможность использования значений в операторах операторов операторов без квалификации.
  5. Встроенная секвенциализация значений через ordinal().
  6. Сериализация по имени не по значению, что обеспечивает степень будущей проверки.
  7. EnumSet и EnumSet .

Никто не упомянул о возможности использовать их в операторах switch ; Я тоже это брошу.

Это позволяет использовать произвольно сложные enums чистым способом, не используя instanceof , потенциально запутывающие последовательности, или значения переключения non-string / int. Канонический пример – это конечный автомат.

Существует меньше путаницы. Возьмите Font например. Он имеет конструктор, который берет имя Font , его размер и стиль ( new Font(String, int, int) ). До сих пор я не помню, идет ли стиль или размер. Если Font использовал enum для всех его разных стилей ( PLAIN , BOLD , ITALIC , BOLD_ITALIC ), его конструктор будет выглядеть как Font(String, Style, int) , предотвращая любую путаницу. К сожалению, enum не было, когда был создан class Font , и поскольку Java должна поддерживать обратную совместимость, мы всегда будем страдать от этой двусмысленности.

Конечно, это просто аргумент для использования enum вместо public static final констант. Перечисления также идеально подходят для одиночных игр и реализации поведения по умолчанию, позволяя использовать более позднюю настройку (IE – шаблон страtagsи ). Примером последнего является OpenOption и StandardOpenOption java.nio.file : если разработчик хотел создать свою собственную нестандартную OpenOption , он мог бы.

Основным преимуществом является безопасность типов. С помощью набора констант можно использовать любое значение одного и того же внутреннего типа, вводя ошибки. С перечислением могут использоваться только применимые значения.

Например

 public static final int SIZE_SMALL = 1; public static final int SIZE_MEDIUM = 2; public static final int SIZE_LARGE = 3; public void setSize(int newSize) { ... } obj.setSize(15); // Compiles but likely to fail later 

против

 public enum Size { SMALL, MEDIUM, LARGE }; public void setSize(Size s) { ... } obj.setSize( ? ); // Can't even express the above example with an enum 

Здесь есть много хороших ответов, но нет упоминаний о том, что существуют высоко оптимизированные реализации classов / интерфейсов Collection API специально для перечислений :

  • EnumSet
  • EnumMap

Эти перечисляемые classы перечислены только для экземпляров Enum ( EnumMap принимает только Enum s только как ключи), и по возможности они возвращаются к компактному представлению и манипуляции с битами в своей реализации.

Что это значит?

Если в нашем типе Enum не более 64 элементов (большинство примеров реального времени Enum для этого), реализации сохраняют элементы в одном long значении, каждый экземпляр Enum идет речь, будет связан с небольшим количеством этого 64- бит длинный long . Добавление элемента в EnumSet просто устанавливает правильный бит в 1, удаляя его, просто устанавливая этот бит в 0. Тестирование, если элемент находится в Set – это всего лишь один битмакс-тест! Теперь вы должны любить Enum для этого!

Первое преимущество перечислений, как вы уже заметили, – простота синтаксиса. Но основным пунктом перечислений является предоставление известного набора констант, которые по умолчанию формируют диапазон и помогают выполнять более полный анализ кода с помощью проверок безопасности типа и ценности.

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

Для компилятора enums помогают определить диапазон значений и, если вы не назначаете специальные значения членам enums, они имеют диапазон от 0 до и выше. Это помогает автоматически отслеживать ошибки в коде с помощью проверок безопасности типа и т. Д. Например, компилятор может предупредить вас о том, что вы не обрабатываете все возможные значения enums в инструкции switch (т. Е. Когда у вас нет случая по default и обрабатывается только одно из N значений enum). Он также предупреждает вас, когда вы конвертируете произвольное целое число в enum, потому что диапазон значений enum меньше, чем integer, и который, в свою очередь, может вызывать ошибки в функции, которая действительно не принимает целое число. Кроме того, создание таблицы перехода для коммутатора становится проще, если значения равны 0 и выше.

Это справедливо не только для Java, но и для других языков со строгой проверкой типов. C, C ++, D, C # – хорошие примеры.

пример:

 public class CurrencyDenom { public static final int PENNY = 1; public static final int NICKLE = 5; public static final int DIME = 10; public static final int QUARTER = 25;} 

Ограничение констант java

1) Нет безопасности типа : Прежде всего, это не безопасно для типов; вы можете присвоить любое допустимое значение int int, например, 99, хотя для представления этого значения нет монеты.

2) Нет значащей печати : значение печати любой из этих констант будет печатать свое числовое значение вместо значимого имени монеты, например, когда вы печатаете NICKLE, он будет печатать «5» вместо «NICKLE»,

3) Нет пространства имен : для доступа к константе currencyDenom нам нужно префиксное имя classа, например CurrencyDenom.PENNY, вместо использования PENNY, хотя это также может быть достигнуто с помощью статического импорта в JDK 1.5

Преимущество enums

1) Перечисления на Java безопасны по типу и имеют собственное пространство имен. Это означает, что ваш перечисление будет иметь тип, например «Валюта» в приведенном ниже примере, и вы не можете назначить какое-либо значение, отличное от указанного в Enum Constants.

 public enum Currency {PENNY, NICKLE, DIME, QUARTER}; 

Currency coin = Currency.PENNY; coin = 1; //compilation error

2) Enum в Java – это ссылочный тип, такой как class или интерфейс, и вы можете определить конструктор, методы и переменные внутри java Enum, который делает его более мощным, чем Enum в C и C ++, как показано в следующем примере типа Java Enum.

3) Вы можете указать значения констант enums во время создания, как показано ниже: public enum Currency {PENNY (1), NICKLE (5), DIME (10), QUARTER (25)}; Но для этого вам нужно определить переменную-член и конструктор, потому что PENNY (1) на самом деле вызывает конструктор, который принимает значение int, см. Ниже пример.

 public enum Currency { PENNY(1), NICKLE(5), DIME(10), QUARTER(25); private int value; private Currency(int value) { this.value = value; } 

};

refrence: http://javarevisited.blogspot.in/2011/08/enum-in-java-example-tutorial.html

перечисление Преимущества:

  1. Перечисления являются безопасными по типу, статические поля не являются
  2. Существует конечное число значений (невозможно передать несуществующее значение enum. Если у вас есть статические classы classов, вы можете сделать эту ошибку)
  3. Каждое перечисление может иметь несколько свойств (полей / геттеров), назначенных – инкапсуляция. Также некоторые простые методы: YEAR.toSeconds () или аналогичные. Сравните: Colors.RED.getHex () с Colors.toHex (Colors.RED)

“, например, возможность легко назначить элементу enums определенное значение”

 enum EnumX{ VAL_1(1), VAL_200(200); public final int certainValue; private X(int certainValue){this.certainValue = certainValue;} } 

“и, следовательно, способность конвертировать целое число в перечисление без приличного усилия” Добавить метод преобразования int в перечисление, которое это делает. Просто добавьте статический HashMap, содержащий отображение.

Если вы действительно хотите преобразовать ord = VAL_200.ordinal () обратно в val_200, просто используйте: EnumX.values ​​() [ord]

Перечисление является окончательно окончательным, с частными конструкторами, все его значения имеют один и тот же тип или подтип, вы можете получить все свои значения с помощью values() , получить его name() или ordinal() значение, или вы можете посмотреть вверх по перечислению по номеру или имени.

Вы также можете определить подclassы (хотя это, конечно, окончательно, то, что вы не можете сделать другим способом)

 enum Runner implements Runnable { HI { public void run() { System.out.println("Hello"); } }, BYE { public void run() { System.out.println("Sayonara"); } public String toString() { return "good-bye"; } } } class MYRunner extends Runner // won't compile. 

Другое важное отличие заключается в том, что java-компилятор рассматривает static final поля примитивных типов и String как литералы. Это означает, что эти константы становятся встроенными. Это похоже на препроцессор C/C++ #define . см. этот вопрос . Это не относится к enumsм.

При использовании enums вы получаете время проверки правильности значений. Посмотрите на этот вопрос.

Самое большое преимущество – перечисление. Синглеты легко записываются и streamобезопасны:

 public enum EasySingleton{ INSTANCE; } 

а также

 /** * Singleton pattern example with Double checked Locking */ public class DoubleCheckedLockingSingleton{ private volatile DoubleCheckedLockingSingleton INSTANCE; private DoubleCheckedLockingSingleton(){} public DoubleCheckedLockingSingleton getInstance(){ if(INSTANCE == null){ synchronized(DoubleCheckedLockingSingleton.class){ //double checking Singleton instance if(INSTANCE == null){ INSTANCE = new DoubleCheckedLockingSingleton(); } } } return INSTANCE; } } 

оба они схожи, и они сами обрабатывали Сериализацию, реализуя

 //readResolve to prevent another instance of Singleton private Object readResolve(){ return INSTANCE; } 

Больше

Я думаю, что enum не может быть final , потому что под капотом компилятор генерирует подclassы для каждой записи enum .

Дополнительная информация Из источника

Обычно это считается плохой практикой. Проблема в том, что константы являются частью публичного «интерфейса» (из-за лучшего слова) реализующего classа. Это означает, что class реализации публикует все эти значения для внешних classов, даже если они необходимы только внутренне. Константы распространяются по всему коду. Примером может служить интерфейс SwingConstants в Swing, который реализуется десятками classов, которые все «реэкспортируют» все свои константы (даже те, которые они не используют) как свои собственные.
Постоянная структура интерфейса плохое использование интерфейсов. То, что class использует некоторые константы внутри, является деталью реализации. Внедрение постоянного интерфейса заставляет эту деталь реализации протекать в экспортированный API classа. Это не имеет никакого значения для пользователей classа, которые class реализует постоянный интерфейс. На самом деле это может даже запутать их. Хуже того, он представляет собой обязательство: если в будущей версии class модифицируется так, что ему больше не нужно использовать константы, он все равно должен реализовать интерфейс для обеспечения совместимости с двоичными файлами. Если нефинальный class реализует константный интерфейс, все его подclassы будут иметь свои пространства имен, загрязненные константами в интерфейсе.
Перечень может быть лучшим подходом. Или вы можете просто поместить константы в качестве общедоступных статических полей в classе, которые не могут быть созданы. Это позволяет другому classу получить к ним доступ, не загрязняя его собственный API.

Interesting Posts
Давайте будем гением компьютера.