¿Por qué enum singleton es vago?
Vi respuestas como estas , traté de aclarar a través de comentarios y no estaba satisfecho con los ejemplos aquí .
Quizás es hora de esta pregunta específica ...
¿Por qué la implementación de enum singleton se llama lazy ?
public enum EnumLazySingleton {
INSTANCE;
EnumLazySingleton() {
System.out.println("constructing: " + this);
}
public static void touchClass() {}
}
¿En qué se diferencia de una implementación entusiasta ?
public class BasicEagerSingleton {
private static final BasicEagerSingleton instance = new BasicEagerSingleton();
public static BasicEagerSingleton getInstance() {
return instance;
}
private BasicEagerSingleton() {
System.out.println("constructing: " + this);
}
public static void touchClass() {}
}
Ambos iniciarán la instancia sin acceder INSTANCE/getInstance(), por ejemplo, llamar touchClass().
public class TestSingleton {
public static void main(String... args) {
System.out.println("sleeping for 5 sec...");
System.out.println("touching " + BasicEagerSingleton.class.getSimpleName());
BasicEagerSingleton.touchClass();
System.out.println("touching " + EnumLazySingleton.class.getSimpleName());
EnumLazySingleton.touchClass();
}
}
Salida:
sleeping for 5 sec...
touching BasicEagerSingleton
constructing: BasicEagerSingleton@7bfcd12c
touching EnumLazySingleton
constructing: INSTANCE
Ahora, podemos decir que ambos son vagos . ¿Qué está ansioso entonces?
Está claro cómo (por ejemplo) la forma de "bloqueo doble verificado" es realmente perezosa (y desordenada, y lenta). Pero si enum es perezoso, entonces cualquier singleton es perezoso debido a la inevitable carga de clases; de hecho, todo es perezoso. ¿En qué momento esta distinción dejará de tener sentido?
Respuestas
Las dos primeras respuestas vinculadas (de Peter Lawrey y Joachim Sauer ) coinciden en que las enumeraciones no se inicializan de forma perezosa. Las respuestas en el tercer enlace son simplemente incorrectas sobre lo que significa la inicialización diferida.
La recomendación de utilizar enumeraciones como singleton se origina en Effective Java de Josh Bloch. En particular, el capítulo sobre los singleton enum no menciona la pereza. Hay un capítulo posterior dedicado a la inicialización diferida, que tampoco menciona las enumeraciones. El capítulo contiene dos aspectos destacados.
- Si necesita utilizar la inicialización diferida para el rendimiento en un campo estático, utilice el idioma de la clase titular de inicialización diferida.
- Si necesita usar la inicialización diferida para el rendimiento en un campo de instancia, use el idioma de verificación doble.
Sin lugar a dudas, las enumeraciones serían otro idioma en esta lista si se inicializaran de manera perezosa. De hecho, no lo son, aunque la confusión sobre el significado de la inicialización perezosa da como resultado algunas respuestas incorrectas, como muestra el OP.
Puedo apostar lo siguiente:
Está tratando de identificar 2 "procesos" o ... "things"(hagamos esto fácil de entender, porque si empiezo a decir "Bloques de código", suena más difícil) ...
- En algún momento se ejecutará el cargador de clases y le gustaría saber qué
"things"se ejecutará cuando el cargador de clases cargue una clase. - En otro punto, la invocación de un método en la clase hará
"thing"que se ejecute / ejecute otro, y le gustaría saber cuál, exactamente, (cuál"processes") comenzaría.
Los siguientes hechos son relevantes:
- Los inicializadores estáticos se ejecutan cuando el cargador de clases carga la clase. El cargador de clases no cargará la clase hasta que el código que se está ejecutando encuentre la necesidad de cargarlo (porque se ha invocado un método o campo) como:
touchClass()- Si una instancia singleton de SEA una clase, o un tipo enumerado tiene una
fieldque está siendo inicializado en lastaticparte de la clase a la que se cargará en cuanto que 'toque' de la clase - porque la clase-Loader funciona todostatic initializationspara una clase o enumeración al cargar.- La carga diferida, probablemente, (y esta es mi "interpretación" sobre lo que está preguntando) sucedería cuando la invocación de un método le pide a la clase que cree una instancia de singleton, lo que podría suceder bastante tiempo después de la "carga" de
classoenum.
Una clase como la siguiente:
public class LazySingleton
{
// At time of class-loading, this singleton is set to 'null'
private static singleton = null;
// This is a method that will not be invoked until it is called by
// some other code-block (some other "thing")... When "touchClass()"
// is called, the singleton instance is not created.
public static LazySingleton retrieveSingleton()
{
if (singleton == null) singleton = new LazySingleton();
return singleton;
}
// DOES NOTHING... The Singleton is *not* loaded, even though the
// Class Loader has already loaded this Java ".class" file
// into memory.
public static void touchClass() { }
private LazySingleton()
{ System.out.println("constructing: LazySingleton"); }
}
Aquí por otro lado:
public enum EagerEnum
{
// The class loader will run this constructor as soon as this 'enum'
// is loaded from a '.class' file (in JAR or on disk) into memory
MyEnumConstant();
private EagerEnum()
{ System.out.println("Eager Enum Constructed"); }
// This will cause the Class Loader to Load this enum from the
// Java ".class" File immediately, and the "MyEnumConstant" will
// also have to be loaded - meaning the constructor will be called.
public static void touchEnum() { }
}
Entonces el siguiente código produciría la salida
LazySingleton.touchClass(); // Prints nothing
EagerEnum.touchClass(); // Prints "Eager Enum Constructed"
LazySingleton.getSingletonInstance(); // Prints "constructing: LazySingleton