阅读 279

[译]Kotlin 中的单例模式

如果我想定义单例模式,那么它应该是这样的:

单例是一种软件设计模式,它保证一个类只有一个实例,并且该类提供对该实例的全局访问控制。

Java 中,通常这样写:

public class Singleton {
    
    private static Singleton instance;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
复制代码

但是上面的代码在多线程场景下是有缺陷的。如果两个线程同时获取实例,则可能会创建两个实例。所以对于上面的代码我们需要改造。

class Singleton {

    private static Singleton instance = null;

    private Singleton() {
    }

    private synchronized static void createInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
    }

    public static Singleton getInstance() {
        if (instance == null) createInstance();
        return instance;
    }
}
复制代码

通过 synchronized 关键字可以确保在创建 instance 时没有线程冲突,只创建一个实例。

Kotlin 中可以这样实现:

class Singleton private constructor() {
    
    private object HOLDER {
        val INSTANCE = Singleton()
    }

    companion object {
        val instance: Singleton by lazy { HOLDER.INSTANCE }
    }
}
复制代码

在上面的例子中,可以看到通过 by lazy{} 确保只在第一次获取实例时触发,即 lazysynchronized 关键字的效果一样。

当然对于单例模式,Kotlin 提供了 object 的实现方式。

object Singleton
复制代码

仅仅一行代码就可以实现单例模式。

object 声明

object DataProviderManager {
    fun registerDataProvider(provider: DataProvider) {
        // ...
    }

    val allDataProviders: Collection<DataProvider>
        get() = // ...
}
复制代码

就像声明变量一样,但是 object 的声明不是表达式,不能进行赋值。object 声明是线程安全的。

要使用 object 声明的对象,可以直接调用。

DataProviderManager.registerDataProvider(...)
复制代码

同样,object 声明的类可以具有父类。

object DefaultListener : MouseAdapter() {
    override fun mouseClicked(e: MouseEvent) { ... }

    override fun mouseEntered(e: MouseEvent) { ... }
}
复制代码

总结

  • Kotlin 通过 object 关键字实现单例;
  • object 类可以包含属性、函数以及 init 块;
  • 不允许有构造函数;
  • 不能以类的实例化方式实例化对象;
  • 当第一次使用对象提供延迟初始化时,该对象将被实例化;
  • object 是线程安全的。