Нещодавно ми з тобою розбиралися, що таке Singleton, як його реалізувати в Java і для чого він потрібен. Але що якщо я тобі розповім, що Java вже має свій синглтон із коробки? Цікаво? Тоді давай розбиратися.

Мабуть, ти вже знаєш про клас enum. Він має особливість, про яку тобі варто знати. Справа в тому, що enum є реалізацією синглтона. Такий варіант майже аналогічний підходу із public полем.

Singelton enum:


public enum Device {   
    PRINTER	
} 
    

Public variable singleton:


public class Printer {   
    public static final Printer PRINTER = new Printer();   
    private Printer() {
    }
//…
}
    

У випадку з публічним полем варіант із enum компактніший — не потрібно писати свою реалізацію. І що найважливіше — у єнамів немає проблем із серіалізацією.

Серіалізація тут працює не так, як для звичайних об’єктів: серіалізується лише значення імені єнаму. У разі десеріалізації метод використовується із десеріалізованим ім’ям, щоб отримати екземпляр. Також enum дозволяє захистити себе від рефлексивних атак.

Більше про Reflection ти дізнаєшся з лекцій другого модуля, в темі Reflection API.

Java представляє заборону на інстанціювання єнамів, і це закодовано в реалізації методу newInstance класу Constructor, який часто викликається під час створення об’єктів через рефлексію.

Частина коду з Constructor.newInstance для enum:


if ((clazz.getModifiers() & Modifier.ENUM) != 0)
    throw new IllegalArgumentException("Cannot reflectively create enum objects");
    

Серед мінусів створення Singleton через enum варто назвати

  • Відсутність лінивої ініціалізації, оскільки об’єкт створюється одразу, і не можна зробити відкладену ініціалізацію.

  • Неможливо розширювати інші класи. Тобто використовувати enum як Singleton у випадках, коли потрібно успадковуватись від іншого класу, не вийде. У таких випадках потрібно звертатися до вже знайомих для нас варіантів реалізації через статичний метод чи публічну змінну.

  • Використовуючи enum як синглтон, ти можеш використовувати тільки одне поле в enum класі.


public enum Device extends Electricity { 
    PRINTER 
}
    

Такий код видасть нам помилку компіляції:

No extends clause allowed for enum

Але якщо нам потрібно реалізувати інтерфейс, немає проблем: enum-и можуть імплементувати інтерфейси:


public enum Device implements Electricity { 
    PRINTER 
}
    

Якщо тобі не потрібно використовувати успадкування, краще використовувати реалізацію Singleton через enum. Так радимо робити не лише ми, а й сам Джошуа Блох.

Використання такої реалізації дає тобі зручність, компактність, серіалізацію із коробки, захист від рефлексивних атак та унікальність, а це те, що потрібно для гарного Синглтона!