LordRaydenMK LordRaydenMK - 27 days ago 32
Android Question

Singleton with parameter in 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 :)

Answer

I am not entirely sure why would you need such code, but here is my best shot at it:

class TasksLocalDataSource private constructor(context: Context) : TasksDataSource {
    private val mDbHelper = TasksDbHelper(context)

    companion object {
        private var instance : TasksLocalDataSource? = null

        fun  getInstance(context: Context): TasksLocalDataSource {
            if (instance == null)
                instance = TasksLocalDataSource(context)

            return instance!!
        }
    }
}

This is similar to what you wrote, and has the same API.

A few notes:

  • Do not use lateinit here. It has a different purpose, and a nullable variable is ideal here.

  • What does checkNotNull(context) do? context is never null here, this is guarantied by Kotlin. All checks and asserts are already implemented by the compiler.

UPDATE:

If all you need is a lazily initialised instance of class TasksLocalDataSource, then just use a bunch of lazy properties (inside an object or on the package level):

val context = ....

val dataSource by lazy {
    TasksLocalDataSource(context)
}
Comments