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?
- ¿Objeto de database Singleton con initialization parametrizada al inicio?
- ¿Cómo crear una instancia de Singleton solo una vez en mi aplicación y sus bibliotecas?
- Serialización Singleton en Kotlin
- ¿Cuál es la forma recomendada de lidiar con la limpieza de Singletons en Android (Kotlin)?
- Usar la habitación como singleton 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.