Kotlin singleton pattern

How to write a singleton pattern in Kotlin

An example of a singleton pattern in Kotlin.

Table of contents for design patterns in Kotlin

In software engineering, the singleton pattern is a software design pattern that restricts the instantiation of a class to one “single” instance. This is useful when exactly one object is needed to coordinate actions across the system. The term comes from the mathematical concept of a singleton. Wikipedia

Example 1:

SingletonHolder

open class SingletonHolder<T: Any, in A>(creator: (A) -> T) {
    private var creator: ((A) -> T)? = creator
    @Volatile protected var instance: T? = null

    fun getInstance(arg: A): T {
        val checkInstance = instance
        if (checkInstance != null) {
            return checkInstance
        }

        return synchronized(this) {
            val checkInstanceAgain = instance
            if (checkInstanceAgain != null) {
                checkInstanceAgain
            } else {
                val created = creator!!(arg)
                instance = created
                creator = null
                created
            }
        }
    }
}

DataBaseConnect

class DataBaseConnect private constructor(private val url: String) {

    fun connect(): String = url

    companion object : SingletonHolder<DataBaseConnect, String>(::DataBaseConnect)
}

Usage:

fun main() {
    val dbA = DataBaseConnect.getInstance("connect url A")
    val dbB = DataBaseConnect.getInstance("connect url B")

    dbA.apply {
        println("${connect()} ${hashCode()}")
    }
    dbB.apply {
        println("${connect()} ${hashCode()}")
    }
}

Result

connect url A 434091818
connect url A 434091818

Example 2:

Singleton

open class Singleton<T : Any>(creator: () -> T) {
    private var creator: (() -> T)? = creator

    @Volatile
    protected var instance: T? = null

    fun get(): T {
        val checkInstance = instance
        if (checkInstance != null) {
            return checkInstance
        }

        return synchronized(this) {
            val checkInstanceAgain = instance
            if (checkInstanceAgain != null) {
                checkInstanceAgain
            } else {
                val created = creator!!()
                instance = created
                creator = null
                created
            }
        }
    }

    companion object {
        fun <T : Any> singletonOf(block: () -> T): Singleton<T> = Singleton(block)
    }
}

ExternalClass

class ExternalClass(val data: String)

Usage:

fun main() {
    val holder = singletonOf { ExternalClass("config data") }
    val dbC = holder.get()
    val dbD = holder.get()

    dbC.apply {
        println("$data ${hashCode()}")
    }
    dbD.apply {
        println("$data ${hashCode()}")
    }
}

Result

config data 985922955
config data 985922955