我怎样才能在 Kotlin 中简化这个单例?

How can I make this singleton simpler in Kotlin?

如何在 Kotlin 中简化这个用于 Android 房间数据库初始化的单例?

@Database(entities = arrayOf(Book::class, User::class), version = 1)
abstract class AppDatabase : RoomDatabase() {

    abstract fun bookModel() : BookDao
    abstract fun userModel() : UserDao

    companion object {
        private var INSTANCE: AppDatabase? = null

        fun getInMemoryDatabase(context: Context): AppDatabase {
            if (INSTANCE == null) {
                INSTANCE = Room.inMemoryDatabaseBuilder(context.applicationContext, AppDatabase::class.java).build()
            }
            return INSTANCE!!
        }

        fun destroyInstance() {
            INSTANCE = null
        }
    }
}

您可以使用数组文字 ([]) 而不是 arrayOf,并且可以使用 elvis 运算符进行空值检查。参见

@Database(entities = [Book::class, User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {

    abstract fun bookModel() : BookDao
    abstract fun userModel() : UserDao

    companion object {
        private var INSTANCE: AppDatabase? = null

        fun getInMemoryDatabase(context: Context): AppDatabase { 
            INSTANCE = INSTANCE ?: Room.inMemoryDatabaseBuilder(context.applicationContext, AppDatabase::class.java).build()
            return INSTANCE!!
        }
    
        fun destroyInstance() {
            INSTANCE = null
        }
    }
}

因为您需要实例,所以您必须将它保存在某个地方,使用 companion object 对我来说似乎是一个合理的解决方案。

如果你不想在 AppDatabase 中保存实例,你也可以使用一个对象(在 Kotlin 中是一个单例)。

object AppDatabaseProvider {
    private var INSTANCE: AppDatabase? = null

    fun getInMemoryDatabase(context: Context): AppDatabase {
       // ...
    }

    fun destroyInstance() {
        INSTANCE = null
    }
}

这些都是在 Kotlin 中处理静态数据的选项,但你不会得到比这短得多的选项。

也许会提前,试试看

@Database(entities = arrayOf(Book::class, User::class), version = 1)
abstract class AppDatabase : RoomDatabase() {

    abstract fun bookModel() : BookDao
    abstract fun userModel() : UserDao

    companion object {
        private var INSTANCE: AppDatabase? = null

        fun getInMemoryDatabase(context: Context): AppDatabase {
            return INSTANCE ?: Room.inMemoryDatabaseBuilder(context, this).build()
        }

        fun destroyInstance() {
            INSTANCE = null
        }
    }
}

您的解决方案基本上没有问题。

使用数组文字是一种选择,正如@Willi Mentzel 所指出的那样,如果您喜欢它的外观,它会使您的代码更短一些。

但是,这段获取单例实例的代码是错误的:

private var INSTANCE: AppDatabase? = null

fun getInMemoryDatabase(context: Context) = INSTANCE ?: Room.inMemoryDatabaseBuilder(context.applicationContext, AppDatabase::class.java).build()

getInMemoryDatabase 函数从不 分配 一个值给 INSTANCE,这意味着它永远是 null,并且每次函数都是调用时,将评估并返回 Elvis 运算符的右侧。这意味着每次都有一个新实例 - 而不是拥有一个单例,您只是使用此代码创建一个工厂。


可以使用 Elvis 运算符稍微缩短您的原始代码:

fun getInMemoryDatabase(context: Context): AppDatabase {
    INSTANCE = INSTANCE ?: Room.inMemoryDatabaseBuilder(context.applicationContext, AppDatabase::class.java).build()
    return INSTANCE!!
}

不过,这会在第一次调用后对自己进行额外的 INSTANCE 赋值,我认为保存这两行可能不值得,因为你的原始代码只有一个常规空检查比这更容易阅读。