How can I make this singleton simpler in Kotlin?

Multi tool use


How can I make this singleton simpler in Kotlin?
How can I make this singleton simpler in Kotlin for the Android room database initialization?
@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
}
}
}
Are you sure that you need an im memory database? It will not be persistent after the app is closed!
– Willi Mentzel
13 hours ago
3 Answers
3
You can use an array literal () instead of
arrayOf
and you can use the elvis operator for the null check. See here.
arrayOf
@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) {
INSTANCE = INSTANCE ?: Room.inMemoryDatabaseBuilder(context.applicationContext, AppDatabase::class.java).build()
return INSTANCE
}
fun destroyInstance() {
INSTANCE = null
}
}
}
Since you need the instance you have to save it somewhere, using a companion object
seems like a reasonable solution to me.
companion object
If you somehow don't want to save the intance inside AppDatabase
, you can also use an object (which is a singleton in Kotlin).
AppDatabase
object AppDatabaseProvider {
private var INSTANCE: AppDatabase? = null
fun getInMemoryDatabase(context: Context) {
// ...
}
fun destroyInstance() {
INSTANCE = null
}
}
These are both options to deal with static data in Kotlin, but you won't get it much shorter than that.
Maybe it will be any advance, try as follow
@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
}
}
}
Your solution is basically fine as is.
Using an array literal is an option, as it's been pointed out by @Willi Mentzel, it makes your code a bit shorter, if you like how it looks.
However, this code for getting the singleton instance is wrong:
private var INSTANCE: AppDatabase? = null
fun getInMemoryDatabase(context: Context) = INSTANCE ?: Room.inMemoryDatabaseBuilder(context.applicationContext, AppDatabase::class.java).build()
The getInMemoryDatabase
function never assigns a value to INSTANCE
, meaning it will forever be null
, and every time the function is called, the right hand side of the Elvis operator will be evaluated and returned. This means a new instance every time - instead of having a singleton, you're just creating a factory with this code.
getInMemoryDatabase
INSTANCE
null
You could use the Elvis operator to shorten your original code a bit:
fun getInMemoryDatabase(context: Context): AppDatabase {
INSTANCE = INSTANCE ?: Room.inMemoryDatabaseBuilder(context.applicationContext, AppDatabase::class.java).build()
return INSTANCE!!
}
This will do an extra assignment of INSTANCE
to itself every call after the first one though, and I'd argue that saving those couple lines might not be worth it, as your original code with just a regular null check was easier to read than this is.
INSTANCE
Ups, you are right! I updated my answer, but using !! is not necessary. The builder will never return null (but throw exeptions instead).
– Willi Mentzel
3 mins ago
I also think, you won't get it much shorter as nhaarman also showed: stackoverflow.com/questions/44315778/…
– Willi Mentzel
1 min ago
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
I had the same issue few days ago.
– Abner Escócio
13 hours ago