在科特林获得单身人士

时间:2018-08-23 14:48:36

标签: kotlin

我一直在查看一些Google示例代码,它们似乎使用以下代码创建了一个单例:

companion object {

        // For Singleton instantiation
        @Volatile
        private var instance: CarRepository? = null

        fun getInstance(carDao: CarDao) =
                instance ?: synchronized(this) {
                    instance ?: CarRepository(carDao).also { instance = it }
                }
    }

所以我知道@Volatile意味着

  

将带注释的属性的JVM支持字段标记为volatile,这意味着对该字段的写入将立即对其他线程可见。

是否应该将所有Singletons实例始终标记为@Volatile?如果是这样,为什么?

最后,我不了解getInstance函数

instance ?: synchronized(this) {
    instance ?: CarRepository(carDao).also { instance = it }
}

这里到底在做什么?

更新: 来源:Google's Sunflower 我更改了存储库和Dao名称供自己使用,但是在Repository文件中却是相同的逻辑。

2 个答案:

答案 0 :(得分:4)

有一个很好的答案here,说明为什么该字段应该是易变的。本质上,没有它,一个线程可能会在实例完全构建之前获得对该实例的引用。

对于getInstance()函数,您具有:

instance ?:

这意味着如果该方法不为null,则它将返回instance,否则将执行?:的右侧。

synchronized(this) {
    instance ?:
}

类似地,在第一次检查实例是否为空之后,在类(companion object)上同步之后,它再次检查非空值,如果可用,则返回它,然后再执行最后一个命令:

CarRepository(carDao).also { instance = it }

这将初始化一个新的CarRepository,然后使用.also块,在返回之前将itCarRepository)分配给instance字段。仅仅因为整个语句是一个表达式,这有点令人困惑。如果您将其设置得更详细,则可能看起来像这样:

fun getInstance(carDao: CarDao): CarRepository {
    var cachedInstance = instance
    if (cachedInstance != null) {
        return cachedInstance
    }

    synchronized(this) {
        cachedInstance = instance
        if (cachedInstance == null) {
            cachedInstance = CarRepository(carDao)
            instance = cachedInstance
        }
        return cachedInstance
    }
}

值得一提的是,我并不真的相信这个特定的例子是一个很好的模式。例如,考虑以下内容:

val carDao1 = CarDaoImpl1()
val carDao2 = CarDaoImpl2()

val carRepo1 = CarRepository.getInstance(carDao1)
val carRepo2 = CarRepository.getInstance(carDao2)

// carRepo2 actually points to carDao1!

答案 1 :(得分:1)

即使这不是真正的单例,我也将尝试解释注释的确切含义:

  fun getInstance(carDao: CarDao) =
         /* if the instance is not null, just return it: */
            instance ?: 
         /* instance is null... enter synchronized block for the first thread... 
            all other threads entering here while the first one is still not finished will block then */
            synchronized(this) {
         /* now the next line is actually here for all the blocked threads... as soon as they are released, they should take the instance that was set by the first thread */
                instance ?: 
         /* the next line actually is only executed by the first thread entering the synchronized-block */
                CarRepository(carDao).also { 
                           /* and this sets the instance that finally is returned by all others */
                             instance = it }
            }

关于@Volatile ...好吧...在这里是这样,以便实例变量实际上可以在随后的线程之间进行同步...以便在第一个线程返回而另一个线程继续进入时可用。同步块。

现在在解释之后:对于Kotlin编写单例的方式,请检查有关Object Expressions, Object Declarations and Companion Objects的Kotlin参考。