'Singleton class in Kotlin

I want to know how to create a singleton class in Kotlin, so that my Util class instantiates it only once per app execution. However, when I converted my Java class to kotlin, the code below was generated.

Is this correct?

companion object {
    private var utilProject: UtilProject? = null

    val instance: UtilProject
        get() {
            if (utilProject == null) utilProject = UtilProject()
            return utilProject!!
        }
} 

I could find a related question, but it is with parameters, and I am not getting it convert without params.



Solution 1:[1]

There is a special keyword object for singletons in Kotlin. You can just type something as simple as this to get working singleton class:

object MySingleton

or when you want some member functions:

object MySingleton {
    fun someFunction(...) {...}
}

And then use it:

MySingleton.someFunction(...)

there is a reference: https://kotlinlang.org/docs/reference/object-declarations.html#object-declarations

EDIT:

In your case, you just need to replace in your definition of class UtilProject to this:

object UtilProject {

    // here you put all member functions, values and variables
    // that you need in your singleton Util class, for example:

    val maxValue: Int = 100

    fun compareInts(a: Int, b: Int): Int {...}
}

And then you can simply use your singleton in other places:

UtilProject.compareInts(1, 2)
//or
var value = UtilProject.maxValue

Solution 2:[2]

Super simple lazy example:

companion object {
    val instance: UtilProject by lazy { UtilProject() }
}

Solution 3:[3]

Only the word object is needed.

object UtilProject {
    var bar: Int = 0
    fun foo() {        
    }
}

And you directly access the object that has only one instance

fun main(args: Array<String>) {
    UtilProject.bar = 1
    println(UtilProject.bar)    
}

Solution 4:[4]

 class TestMySingleton private constructor() {
?
   companion object {
        var single = TestMySingleton()

        fun getInstance(): TestMySingleton {
            if (single == null)
                single = TestMySingleton()
            return single
        }
    }

}

Solution 5:[5]

A Singleton example over retrofit to support the api call.

object RetrofitClient {

    private var instance: Api? = null
    private val BASE_URL = "https://jsonplaceholder.typicode.com/"

    fun getInstance(): Api? {
        if (instance == null) {
            val retrofit = Retrofit.Builder()
                    .baseUrl(BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build()
            instance = retrofit.create(Api::class.java)
        }
        return instance
    }
}

Solution 6:[6]

Variant with parametrs

open class SingletonHolder<out T: Any, in A>(creator: (A) -> T) {
    private var creator: ((A) -> T)? = creator
    @Volatile private 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
            }
        }
    }
}


Solution 7:[7]

class MyClass {


    init {
        println("init is called")
    }

    companion object {

        private var obj: MyClass? = null
        fun getInstance(): MyClass {
            if (obj == null) {
                obj = MyClass()
            }
            return obj as MyClass 
        }

    }

    fun printHello() {
        println("Hello World")
    }

You can create its instance by MyClass.getInstance() something like java

Solution 8:[8]

This will help. I am using Dialog class, but you can use the example for the idea on how to implement.

class MyClass(context: Context) : Dialog(context) {
    companion object {
    lateinit var INSTANCE: MyClass

    @JvmStatic
    fun getInstance(context: Context): MyClass{
        if (!::INSTANCE.isInitialized) {
            INSTANCE = MyClass(context)
        }

        return INSTANCE
    }
}}

Solution 9:[9]

All the answers here are mostly correct except when thread handling comes. My use case was this

Calling both methods at the same time with different threads:

private fun getProductListSync() {
    launch(Dispatchers.Main) {
        products = withContext(Dispatchers.IO) { getProducts() }
    }
}

private suspend fun getProducts(): List<Product>? {
    val client = APIUtils.getClient() // this method is used for getting Retrofit Client
    val productListCall = client.create(APIBuilder::class.java).getProductList()
    return if (productListCall.isSuccessful) {
        ...
    } else {
        ...
    }
}

private fun getRestaurantDetailsSync() {
    launch(Dispatchers.Main) {
        storeInfo = withContext(Dispatchers.IO) { getStoreInfo() }
    }
}

private suspend fun getStoreInfo(): StoreInfo? {
    val client = APIUtils.getClient()
    val storeInfoCall = client.create(APIBuilder::class.java).getStoreInfo()
    return if (storeInfoCall.isSuccessful) {
        ...
    } else {
        ...
    }
}

Calling Code:

getRestaurantDetailsSync()
getProductListSync()

Correct code for APIUtils for singleton pattern for multiple thread handling

APIUtils.kt

object APIUtils {

    @Volatile
    private var retrofit: Retrofit? = null

    /**
     * You can create multiple methods for different BaseURL
     *
     * @return [Retrofit] object
     */
    @Synchronized
    fun getClient(): Retrofit {
        if (retrofit == null) {
            retrofit = Builder()
                .baseUrl(Constants.API.BASE_URL)
                .build()
        }
        return retrofit!!
    }

    fun destroy() {
        retrofit = null
    }
}

Note: Here, if we don't use @Volatile on field and @Synchronized on function it will create multiple copies of retrofit field when called from different threads.

You can also reassign retrofit client to apply additional static headers because we used "var" keyword instead of "val" or "lateinit var"

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Micha? Pow?oka
Solution 3 Gustavo Wilgenhoff
Solution 4
Solution 5 onCompletion
Solution 6 Fury Safik
Solution 7 abhi
Solution 8 Harpreet
Solution 9 Raghav Satyadev