Навіщо використовувати будь-яку функцію мови програмування? Причина, з якої ми взагалі володіємо мовами, - це
- Програмісти для ефективного та правильного вираження алгоритмів у формі можуть використовувати комп’ютери.
- Обслуговуючі, щоб зрозуміти алгоритми, які інші написали та правильно внесли зміни.
Енуми покращують як вірогідність коректності, так і читабельності без написання багато табличок. Якщо ви готові написати котельну табличку, тоді ви можете "імітувати" перерахунки:
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 };
Обидва надають однаковий рівень перевірки допомоги від компілятора. Котельня плита просто більше набирається. Але економія багато набравши тексту робить програміста більш ефективним (див. 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 як синглтон
З котла вище можна зрозуміти, чому 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
це буде працювати з комутатором, але воно настільки складне, що це ще більше чітко показує, чому 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
. Ви отримуєте ліниво ініціалізований синглтон з дуже маленьким кодом. Єдиний мінус - досить незрозумілий синтаксис. Вам потрібно знати ідіому або досконало зрозуміти, як працює завантаження та ініціалізація класів, щоб знати, що відбувається.