95

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.

Paulo Merson
  • 13,270
  • 8
  • 79
  • 72
Khemraj Sharma
  • 57,232
  • 27
  • 203
  • 212

10 Answers10

107

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
dey
  • 3,022
  • 1
  • 15
  • 25
  • I did not get this. How can this be useful for my Util class? – Khemraj Sharma Aug 14 '18 at 07:00
  • against creating `companion object`, just change declaration of `class UtilProject` to `object UtilProject` – dey Aug 14 '18 at 07:16
  • Like @Naetmul answer? – Khemraj Sharma Aug 14 '18 at 07:18
  • 4
    actually, using `object` instead of `class` is your singleton. The [mentioned reference above](https://kotlinlang.org/docs/reference/object-declarations.html#object-declarations) is worth reading ;-) – Roland Aug 14 '18 at 07:21
  • How we can create a singleton(object) class with an extended base class? Also, the base class is expecting some context as constructor parameter? Is this possible?. In my understanding, an object class won't have a constructor. – Nithinjith Jun 26 '19 at 11:42
  • As far as I understand, you want to create singleton which extends some other class, you can simply do that just by doing this: `object MySingleton: BaseClass("some value")` where `abstract class BaseClass(val value: String)` of course your `BaseClass` don't have to be abstract. – dey Jun 27 '19 at 09:11
  • 2
    Using `object` instead of `class` is the most optimized solution. If you want to call a method inside your object from a JAVA class you can use `Singleton.INSTANCE.methodeName()..` – Ahmed Nezhi Jul 12 '21 at 10:03
88

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."

Naetmul
  • 14,544
  • 8
  • 57
  • 81
  • Are all objects inside `companion` singleton? – Khemraj Sharma Aug 14 '18 at 06:43
  • @Khemraj No. The `companion object` is one singleton object. If you need many different singletons, you can create many objects for each singleton. I will update the answer. – Naetmul Aug 14 '18 at 06:44
  • Good explanation !, Still I need a year to understand this new language syntax. – Khemraj Sharma Aug 14 '18 at 09:44
  • That's not the best answer, because the best way to create a util class is to use object keyword in place of class keyword - then it is a language level singleton, and we don't have to bother with creating an instance of such a class. We just use functions the same way as Java's static methods. Please see my answer – dey Jun 21 '20 at 01:38
  • ```The companion object is one singleton object. If you need many different singletons, you can create many objects for each singleton.``` - hehe, nice joke bro – i30mb1 Nov 24 '20 at 08:11
  • 1
    What about INITIALIZATION of the singleton? If `ojbect` can't have a constructor, should we use an initializer block (`init {...}`)? – Paulo Merson Dec 14 '20 at 19:40
  • 1
    `object or companion object is instantiated when it is first used` this isn't correct, `object` is, but `companion object` is when the class is loaded. – William Hu Feb 20 '22 at 16:05
14

Super simple lazy example:

companion object {
    val instance: UtilProject by lazy { UtilProject() }
}
Michał Powłoka
  • 1,424
  • 1
  • 13
  • 31
8

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)    
}
3
 class TestMySingleton private constructor() {
​
   companion object {
        var single = TestMySingleton()

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

}
Joyal C Joseph
  • 290
  • 2
  • 12
3

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
    }
}}
Harpreet
  • 2,990
  • 3
  • 38
  • 52
2

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
    }
}
onCompletion
  • 6,500
  • 4
  • 28
  • 37
Abhishek Rana
  • 321
  • 2
  • 3
  • 1
    The way you're implementing the `getInstance()` method is not correct. The logic inside `getInstance()` says it will always return `non-null` value yet your return type (`Api?`) says it can return `null` value. If you try to refactor return type to `Api` you'll get `Smart cast to a mutable property is not possible` warning. – Farid Dec 10 '19 at 16:47
  • This is a flawed implementation. It's missing synchronization. There's also no reason it should have to return a nullable. – Tenfour04 Nov 14 '22 at 19:10
2

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


Fury Safik
  • 21
  • 1
  • 2
    A great answer but it's only a part of the whole thing. This code is copied from here: https://blog.mindorks.com/how-to-create-a-singleton-class-in-kotlin Take a look to get the whole picture and why this is the way to handle it. – C.Schone Jun 20 '21 at 20:29
1
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

abhi
  • 961
  • 9
  • 13
0

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"

Raghav Satyadev
  • 728
  • 2
  • 11
  • 38