Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Singleton with parameter in Kotlin

Tags:

android

kotlin

I am trying to convert an Android app from Java to Kotlin. There are a few singletons in the app. I used a companion object for the singletons without constructor parameters. There is another singleton that takes a constructor parameter.

Java code:

public class TasksLocalDataSource implements TasksDataSource {      private static TasksLocalDataSource INSTANCE;      private TasksDbHelper mDbHelper;      // Prevent direct instantiation.     private TasksLocalDataSource(@NonNull Context context) {         checkNotNull(context);         mDbHelper = new TasksDbHelper(context);     }      public static TasksLocalDataSource getInstance(@NonNull Context context) {         if (INSTANCE == null) {             INSTANCE = new TasksLocalDataSource(context);         }         return INSTANCE;     } } 

My solution in kotlin:

class TasksLocalDataSource private constructor(context: Context) : TasksDataSource {      private val mDbHelper: TasksDbHelper      init {         checkNotNull(context)         mDbHelper = TasksDbHelper(context)     }      companion object {         lateinit var INSTANCE: TasksLocalDataSource         private val initialized = AtomicBoolean()          fun getInstance(context: Context) : TasksLocalDataSource {             if(initialized.getAndSet(true)) {                 INSTANCE = TasksLocalDataSource(context)             }             return INSTANCE         }     } } 

Am I missing anything? Thread safety? Laziness ?

There were a few similar questions but I don't like the answers :)

like image 830
LordRaydenMK Avatar asked Nov 03 '16 09:11

LordRaydenMK


People also ask

How would you create a singleton with parameter in Kotlin?

In Kotlin, we need to use the object keyword to use Singleton class. The object class can have functions, properties, and the init method. The constructor method is not allowed in an object so we can use the init method if some initialization is required and the object can be defined inside a class.

Can singleton constructor have parameters?

However, singleton is a principle in Java that can only be created when: A private class has a default constructor. Any protected static class type object is declared with the null value. A parameter is assigned to the constructor of the singleton class type (as we did in step two).

Can Kotlin object have constructor?

In Kotlin, a class can have a primary constructor and one or more additional secondary constructors.

How do you make a private constructor in Kotlin?

This is possible using a companion object: class Foo private constructor(val someData: Data) { companion object { fun constructorA(): Foo { // do stuff return Foo(someData) } } // ... } Adding to this for completeness. From Java you can only call methods inside a companion object only with Companion like Foo.


2 Answers

Here's a neat alternative from Google's architecture components sample code, which uses the also function:

class UsersDatabase : RoomDatabase() {      companion object {          @Volatile private var INSTANCE: UsersDatabase? = null          fun getInstance(context: Context): UsersDatabase =             INSTANCE ?: synchronized(this) {                 INSTANCE ?: buildDatabase(context).also { INSTANCE = it }             }          private fun buildDatabase(context: Context) =             Room.databaseBuilder(context.applicationContext,                     UsersDatabase::class.java, "Sample.db")                     .build()     } } 
like image 194
fal Avatar answered Sep 17 '22 22:09

fal


Thread-Safe Solution # Write Once; Use Many;

It's a good solution to create a class implementing the logic of singleton which also holds the singleton instance, like the following.

It instantiates the instance using Double-Check Locking in a synchronized block to eliminate possibility of race condition in multi-threaded environments.

SingletonHolder.kt

open class SingletonHolder<out T, in A>(private val constructor: (A) -> T) {      @Volatile     private var instance: T? = null      fun getInstance(arg: A): T =         instance ?: synchronized(this) {             instance ?: constructor(arg).also { instance = it }         } } 

Usage

Now in each class that you want to be singleton, write a companion object extending the above class. SingletonHolder is a generic class that accepts type of target class and its requiring parameter as generic params. It also needs a reference to the constructor of target class which is used for instantiating an instance:

class MyManager private constructor(context: Context) {      fun doSomething() {         ...     }      companion object : SingletonHolder<MyManager, Context>(::MyManager) } 

Finally:

MyManager.getInstance(context).doSomething() 
like image 45
aminography Avatar answered Sep 16 '22 22:09

aminography