Recientemente exploraste el patrón de diseño singleton, cómo implementarlo en Java y para qué sirve. ¿Pero qué pasaría si te dijera que Java ya viene con su propio singleton incluido? ¿Intrigado? Entonces adéntrate en este tema.

Probablemente ya conoces la clase Enum. Tiene una característica especial que deberías conocer. Específicamente, Enum implementa el patrón de diseño singleton. Esta opción es casi igual que el enfoque singleton que implica un campo public.

Singleton como enum:


public enum Device {   
    PRINTER	
} 
    

Singleton as a public variable:


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

La aproximación con enum es más compacta que la aproximación con campo público, ya que no necesitamos escribir nuestra propia implementación. Lo más importante es que los enums no tienen problemas con la serialización.

La serialización de enums funciona de manera diferente a la de los objetos ordinarios: solo se serializa el valor del nombre del enum. Durante la deserialización, se utiliza el método con el nombre deserializado para obtener una instancia. Además, enum puede protegerte contra los ataques de reflexión.

Aprenderás más sobre la reflexión en las lecciones del segundo módulo, donde exploraremos la API de reflexión.

Java prohíbe la instanciación de enums, una limitación incorporada en la implementación del método newInstance de la clase Constructor, que a menudo se llama al crear objetos mediante la reflexión.

Extracto de código de Constructor.newInstance. Se utiliza para crear un enum:


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

Las desventajas de utilizar un enum para crear un singleton incluyen:

  • Falta de inicialización diferida, ya que el objeto se crea inmediatamente y la inicialización no se puede retrasar.

  • Otras clases no se pueden extender. Es decir, en casos en los que se necesita heredar de otra clase, no funciona utilizar un enum como un singleton. En esos casos, necesitamos recurrir a las otras opciones de implementación que ya conocemos: un método estático o una variable pública.

  • Cuando se utiliza enum como un singleton, sólo se puede utilizar un campo enum.


public enum Device extends Electricity { 
    PRINTER 
}
    

Este código nos dará un error de compilación:

No se permite la cláusula extends para enum

Pero si necesitamos implementar una interfaz, no hay problema, ya que un enum puede implementar interfaces:


public enum Device implements Electricity { 
    PRINTER 
}
    

Si no necesitas usar la herencia, lo mejor es implementar el patrón singleton a través de enum. No estamos solos en recomendar esto, Joshua Bloch también lo hace.

Este enfoque de implementación te brinda comodidad, compacidad, serialización integrada, protección contra ataques de reflexión y singularidad, todo lo que un buen singleton necesita!