Implementando Singleton con un Enum (en Java)

He leído que es posible implementar Singleton en Java usando un Enum como:

 public enum MySingleton { INSTANCE; } 

Pero, ¿cómo funciona lo anterior? Específicamente, un Object debe ser instanciado. Aquí, ¿cómo se está instanciando MySingleton ? ¿Quién está haciendo new MySingleton() ?

Esta,

 public enum MySingleton { INSTANCE; } 

tiene un constructor implícito vacío. Seamos explícitos en cambio,

 public enum MySingleton { INSTANCE; private MySingleton() { System.out.println("Here"); } } 

Si luego agregaste otra clase con un método main() como

 public static void main(String[] args) { System.out.println(MySingleton.INSTANCE); } 

Tu verias

 Here INSTANCE 

enum campos enum son constantes de tiempo de comstackción, pero son instancias de su tipo enum . Y, se construyen cuando se hace referencia al tipo enum por primera vez.

En este libro de buenas prácticas de Java escrito por Joshua Bloch, puede encontrar explicaciones sobre por qué debe aplicar la propiedad Singleton con un constructor privado o un tipo Enum. El capítulo es bastante largo, por lo que lo mantendremos resumido:

Hacer una clase en Singleton puede dificultar la prueba de sus clientes, ya que es imposible sustituir una implementación simulada por una singleton a menos que implemente una interfaz que sirva como su tipo. El enfoque recomendado es implementar Singletons simplemente haciendo un tipo de enumeración con un elemento:

 // Enum singleton - the preferred approach public enum Elvis { INSTANCE; public void leaveTheBuilding() { ... } } 

Este enfoque es funcionalmente equivalente al enfoque de campo público, excepto que es más conciso, proporciona la maquinaria de serialización de forma gratuita y proporciona una garantía inquebrantable contra la creación de instancias múltiples, incluso frente a sofisticados ataques de serialización o reflexión.

Si bien este enfoque aún no se ha adoptado ampliamente, un tipo de enumeración de elemento único es la mejor manera de implementar un singleton.

Un tipo enum es un tipo especial de tipo de class .

Su statement enum realmente se comstack a algo como

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

Cuando su código acceda primero a INSTANCE , la clase MySingleton será cargada e inicializada por la JVM. Este proceso inicializa el campo static arriba una vez (perezosamente).

Al igual que todas las instancias enum, Java crea una instancia de cada objeto cuando se carga la clase, con alguna garantía de que se crea una instancia exactamente una vez por JVM . Piense en la statement INSTANCE como un campo final público estático: Java instanciará el objeto la primera vez que se haga referencia a la clase.

Las instancias se crean durante la inicialización estática, que se define en la Especificación del lenguaje Java, sección 12.4 .

Por lo que vale, Joshua Bloch describe este patrón en detalle como el ítem 3 de Effective Java Second Edition .

Como Singleton Pattern trata sobre tener un constructor privado y llamar a algún método para controlar las instancias (como algunos getInstance ), en Enums ya tenemos un constructor privado implícito.

No sé exactamente cómo la JVM o algún contenedor controla las instancias de nuestros Enums , pero parece que ya usa un Singleton Pattern implícito, la diferencia es que no llamamos a getInstance , simplemente llamamos a Enum.

Como se ha mencionado anteriormente, una enumeración es una clase Java con la condición especial de que su definición debe comenzar con al menos una “constante enum”.

Parte de eso, es una clase como cualquier clase y la usas al agregar métodos debajo de las definiciones constantes:

 public enum MySingleton { INSTANCE; public void doSomething() { ... } public synchronized String getSomething() { return something; } private String something; } 

Usted accede a los métodos de Singleton en esta línea:

 MySingleton.INSTANCE.doSomething(); String something = MySingleton.INSTANCE.getSomething(); 

El uso de una enumeración, en lugar de una clase, es, como se ha mencionado en otras respuestas, principalmente sobre una instanciación segura de subprocesos del singleton y una garantía de que siempre será una sola copia.

Y, quizás, lo más importante, que este comportamiento esté garantizado por la propia JVM y la especificación de Java.

Aquí hay una sección de la especificación de Java sobre cómo se previenen múltiples instancias de una instancia enum:

Un tipo enum no tiene más instancias que las definidas por sus constantes enum. Es un error en tiempo de comstackción intentar instanciar explícitamente un tipo de enumeración. El método de clonación final en Enum garantiza que las constantes de la enumeración nunca se puedan clonar, y el tratamiento especial del mecanismo de serialización garantiza que las instancias duplicadas nunca se creen como resultado de la deserialización. La ejemplificación reflexiva de los tipos enum está prohibida. En conjunto, estas cuatro cosas aseguran que no existan instancias de un tipo de enumeración más allá de las definidas por las constantes enum.

Cabe destacar que, después de la instanciación, cualquier problema relacionado con la seguridad debe manejarse como en cualquier otra clase con la palabra clave sincronizada, etc.