Kotlin线程使用参数保存本机懒惰单例

在java中,我们可以使用double Checked Locking&volatile来编写单独的save-singletons:

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; } } 

我们如何写在kotlin?


关于对象

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

我使用kotlin反编译器来获取

 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(); } } } 

所有对象都有构造函数在static块中调用。 基于此,我们可以认为这不是懒惰。

输入正确的答案。

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

反编译:

 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(); } } 

我希望Kotlin开发人员将来可以不做反思

Kotlin与Java代码相当,但更安全。 即使对于Java,也不建议您进行双重锁定检查 。 在Java中,您应该使用静态的内部类,这也在按需初始化持有人惯用语中进行了解释 。

但那是Java。 在Kotlin中,只需使用一个对象 (一个可选的懒惰委托):

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

您可以访问任何成员变量:

 // 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有意避免了人们在Java中使用单例的困惑。 并避免这种模式的“错误版本” – 其中有很多。 它反而提供了更简单和最安全的单身形式。

鉴于lazy()的使用,如果你有其他成员每个将单独懒惰。 而且,因为它们在传递给lazy()的lambda中进行了初始化,所以您可以执行关于自定义构造函数和每个成员属性的信息。

因此,你可以延迟加载Singletons对象( 在第一次访问实例时 ),然后加载something第一次访问成员 ),并在对象构造方面具有完全的灵活性。

也可以看看:

  • lazy()函数
  • 懒惰的线程安全模式选项
  • 对象声明

作为一个方面说明,查看Kotlin的对象注册表类型库,它与依赖注入类似,为您提供带有注入选项的单例:

  • Injekt – 我是作者
  • Kodein – 非常相似,很好

对象声明正是为了这个目的:

 object Singleton { //singleton members } 

这是懒惰和线程安全的,它首次调用时初始化,很像Java的静态初始化。

您可以在顶层或者在类或其他对象中声明一个对象。

有关使用Java处理object的更多信息,请参阅此答案 。


至于参数,如果你想实现完全相同的语义(首先调用getInstance接受它的参数初始化单例,下面的调用只是返回实例,删除参数),我会建议这个结构:

 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 } } 

这个解决方案的主要缺陷是它需要在单独的文件中定义单例以隐藏object SingletonInit ,并且在初始化之前不能直接引用Singleton

另外,请参阅有关为单例提供参数的类似问题 。

我最近写了一篇关于这个话题的文章 。 TL; DR下面是我提出的解决方案:

1)创建一个SingletonHolder类。 你只需要写一次:

 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)在你的单身人士中这样使用它:

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

单例初始化将是懒惰和线程安全的。