Kotlin thread salva el singleton perezoso nativo con parámetro

En java podemos escribir los save-save singletons con doble checked Locking y volátil:

public class Singleton { private static volatile Singleton instance; public static Singleton getInstance(String arg) { Singleton localInstance = instance; if (localInstance == null) { synchronized (Singleton.class) { localInstance = instance; if (localInstance == null) { instance = localInstance = new Singleton(arg); } } } return localInstance; } } 

¿Cómo podemos escribirlo en kotlin?


Sobre el object

 object A { object B {} object C {} init { C.hashCode() } } 

Utilicé el descomstackdor de Kotlin para get ese

 public final class A { public static final A INSTANCE; private A() { INSTANCE = (A)this; ACINSTANCE.hashCode(); } static { new A(); } public static final class B { public static final AB INSTANCE; private B() { INSTANCE = (AB)this; } static { new AB(); } } public static final class C { public static final AC INSTANCE; private C() { INSTANCE = (AC)this; } static { new AC(); } } } 

Todos los objects tienen invocación de constructor en bloque static . Basado en eso, podemos pensar que no es flojo.

Pierde la respuesta correcta.

  class Singleton { companion object { val instance: Singleton by lazy(LazyThreadSafetyMode.PUBLICATION) { Singleton() } } } 

Descomstackdo:

 public static final class Companion { // $FF: synthetic field private static final KProperty[] $$delegatedProperties = new KProperty[]{(KProperty)Reflection.property1(new PropertyReference1Impl(Reflection.getOrCreateKotlinClass(Singleton.Companion.class), "instance", "getInstance()Lru/example/project/tech/Singleton;"))}; @NotNull public final Singleton getInstance() { Lazy var1 = Singleton.instance$delegate; KProperty var3 = $$delegatedProperties[0]; return (Singleton)var1.getValue(); } private Companion() { } // $FF: synthetic method public Companion(DefaultConstructorMarker $constructor_marker) { this(); } } 

Espero que los desarrolladores de Kotlin hagan una implementación sin reflexión en el futuro …

Kotlin tiene un equivalente de tu código de Java, pero es más seguro. Su doble locking no es recomendable incluso para Java . En Java, debe utilizar una class interna en la estática, que también se explica en la initialization a pedido idioma del titular .

Pero eso es Java. En Kotlin, simplemente use un object (un delegado opcionalmente flojo):

 object Singletons { val something: OfMyType by lazy() { ... } val somethingLazyButLessSo: OtherType = OtherType() val moreLazies: FancyType by lazy() { ... } } 

A continuación, puede acceder a cualquier variable miembro:

 // Singletons is lazy instantiated now, then something is lazy instantiated after. val thing = Singletons.something // This is Doubly Lazy! // this one is already loaded due to previous line val eager = Singletons.somethingLazyButLessSo // and Singletons.moreLazies isn't loaded yet until first access... 

Kotlin evita intencionalmente la confusión que la gente tiene con los singleton en Java. Y evita las "versiones incorrectas" de este patrón, de las cuales hay muchas. En su lugar, proporciona la forma más simple y segura de singletons.

Dado el uso de lazy() , si tiene otros miembros, cada uno sería perezoso individualmente. Y dado que se inicializaron en el lambda pasado a lazy() , puede hacer las cosas que preguntaba sobre la personalización del constructor y de cada propiedad del miembro.

Como resultado, tiene la carga diferida del object Singletons ( en el primer acceso de la instancia ), y luego la carga más lenta de something ( en el primer acceso del miembro ) y completa flexibilidad en la construcción del object.

Ver también:

  • lazy()
  • Opciones de modo seguro de subprocesss diferidos
  • Declaraciones de objects

Como nota al margen, busque en las bibliotecas de types de logging de object para Kotlin que son similares a la dependency injection, que le da singletons con opciones de inyección:

  • Injekt – Soy el autor
  • Kodein – Muy similar y bueno

La statement del object es exactamente para este propósito:

 object Singleton { //singleton members } 

Es flojo y seguro para subprocesss, se inicializa en la primera llamada, al igual que los inicializadores estáticos de Java.

Puede declarar un object en el nivel superior o dentro de una class u otro object.

Para get más información sobre cómo trabajar con object s de Java, consulte esta respuesta .


En cuanto al parámetro, si quiere lograr exactamente la misma semántica (la primera llamada a getInstance toma su argumento para inicializar el singleton, las siguientes llamadas simplemente devuelven la instancia, soltando los arguments), sugeriría este constructo:

 private object SingletonInit { //invisible outside the file lateinit var arg0: String } object Singleton { val arg0: String = SingletonInit.arg0 } fun Singleton(arg0: String): Singleton { //mimic a constructor, if you want synchronized(SingletonInit) { SingletonInit.arg0 = arg0 return Singleton } } 

El principal error de esta solución es que requiere que el singleton se defina en un file separado para ocultar el object SingletonInit , y no se puede hacer reference a Singleton directamente hasta que se inicialice.

Además, vea una pregunta similar acerca de proporcionar arguments a un singleton.

Recientemente escribí un artículo sobre ese tema . TL; DR Aquí está la solución que surgió:

1) Crea una class SingletonHolder . Solo tienes que escribirlo una vez:

 open class SingletonHolder<out T, in A>(creator: (A) -> T) { private var creator: ((A) -> T)? = creator @Volatile private var instance: T? = null fun getInstance(arg: A): T { val i = instance if (i != null) { return i } return synchronized(this) { val i2 = instance if (i2 != null) { i2 } else { val created = creator!!(arg) instance = created creator = null created } } } } 

2) Úselo así en sus singletons:

 class MySingleton private constructor(arg: ArgumentType) { init { // Init using argument } companion object : SingletonHolder<MySingleton, ArgumentType>(::MySingleton) } 

La initialization de singleton será floja y segura para subprocesss.