Singleton with Parameter in Kotlin

Singleton with parameter in Kotlin

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()
}
}

Singleton with argument in Kotlin

Since objects do not have constructors what I have done the following to inject the values on an initial setup. You can call the function whatever you want and it can be called at any time to modify the value (or reconstruct the singleton based on your needs).

object Singleton {
private var myData: String = ""

fun init(data: String) {
myData = data
}

fun singletonDemo() {
System.out.println("Singleton Data: ${myData}")
}
}

Singleton class in Kotlin

Just

companion object {
val instance = UtilProject()
}

will do the job because the companion object itself is a language-level singleton.

(The instance will be created when the companion object is first called.)

-- Updated --

If you need to control when the singleton object is initialized, you can create one object for each class.

class UtilProject {
....
companion object {
val instance = UtilProject()
}
}

class AnotherClass {
...
companion object {
val instance = AnotherClass()
const val abc = "ABC"
}
}

fun main(args: Array<String>) {
val a = UtilProject.instance // UtilProject.instance will be initialized here.
val b = AnotherClass.abc // AnotherClass.instance will be initialized here because AnotherClass's companion object is instantiated.
val c = AnotherClass.instance
}

Here, AnotherClass.instance is initialized before AnotherClass.instance is actually called. It is initialized when AnotherClass's companion object is called.
To prevent it from being initialized before when it is needed, you can use like this:

class UtilProject {
....
companion object {
fun f() = ...
}
}

class AnotherClass {
...
companion object {
const val abc = "ABC"
}
}

object UtilProjectSingleton {
val instance = UtilProject()
}

object AnotherClassSingleton {
val instance = AnotherClass()
}

fun main(args: Array<String>) {
UtilProject.f()
println(AnotherClass.abc)

val a = UtilProjectSingleton.instance // UtilProjectSingleton.instance will be initialized here.
val b = AnotherClassSingleton.instance // AnotherClassSingleton.instance will be initialized here.

val c = UtilProjectSingleton.instance // c is a.
}

If you don't care when each singleton is initialized, you can also use it like this:

class UtilProject {
....
companion object {
fun f() = ...
}
}

class AnotherClass {
...
companion object {
const val abc = "ABC"
}
}

object Singletons {
val utilProject = UtilProject()
val anotherClass = AnotherClass()
}

fun main(args: Array<String>) {
val a = Singletons.utilProject
val b = Singletons.anotherClass
}

In summary,

an object or a companion object is one singleton object in Kotlin.

You can assign variables in an object or objects, and then use the variables just like they were singletons.

object or companion object is instantiated when it is first used.
vals and vars in an object are initialized when the object is first instantiated (i.e., when the object is first used).

EDIT:
William Hu said in the comment that "a companion object is when the class is loaded."

Kotlin Singletons: Object vs a Class with private constructor

Using an object is an issue if your singleton instance needs parameters, like in this case here, with GardenPlantingDao, as they cannot take constructor arguments. This comes up frequently on Android, as there's many cases where singletons requires a Context to operate.

You could still use an object in these cases, but it would either be unsafe or inconvenient:

  • The first option would be to provide it with its dependencies using a setter method before using any of its other methods. This would mean that every other method would have to check if the dependencies have been initialized, and probably throw exceptions in case they haven't, which leads to runtime problems.
  • Alternatively, you could require any dependencies as arguments to each method of the singleton, which is tedious at the call site.

Hence the "traditional" way of implementing a singleton with a private constructor and a factory method instead.

Kotlin thread safe native lazy singleton with parameter

Kotlin has an equivalent of your Java code, but more safe. Your double lock check is not recommended even for Java. In Java you should use an inner class on the static which is also explained in Initialization-on-demand holder idiom.

But that's Java. In Kotlin, simply use an object (and optionally a lazy delegate):

object Singletons {
val something: OfMyType by lazy() { ... }

val somethingLazyButLessSo: OtherType = OtherType()
val moreLazies: FancyType by lazy() { ... }
}

You can then access any member variable:

// Singletons is lazy instantiated now, then something is lazy instantiated after.  
val thing = Singletons.something // This is Doubly Lazy!

// this one is already loaded due to previous line
val eager = Singletons.somethingLazyButLessSo

// and Singletons.moreLazies isn't loaded yet until first access...

Kotlin intentionally avoids the confusion people have with singletons in Java. And avoids the "wrong versions" of this pattern -- of which there are many. It instead provides the simpler and the safest form of singletons.

Given the use of lazy(), if you have other members each would individually be lazy. And since they are initialized in the lambda passed to lazy() you can do things that you were asking about for about customizing the constructor, and for each member property.

As a result you have lazy loading of Singletons object (on first access of instance), and then lazier loading of something (on first access of member), and complete flexibility in object construction.

See also:

  • lazy() function
  • Lazy thread safe mode options
  • Object declarations

As a side note, look at object registry type libraries for Kotlin that are similar to dependency injection, giving you singletons with injection options:

  • Injekt - I'm the author
  • Kodein - Very similar and good


Related Topics



Leave a reply



Submit