Singleton class in Kotlin
AndroidKotlinAndroid Problem Overview
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.
Android Solutions
Solution 1 - Android
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 - Android
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.
val
s and var
s in an object
are initialized when the object
is first instantiated (i.e., when the object
is first used).
Solution 3 - Android
Super simple lazy example:
companion object {
val instance: UtilProject by lazy { UtilProject() }
}
Solution 4 - Android
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 5 - Android
class TestMySingleton private constructor() {
companion object {
var single = TestMySingleton()
fun getInstance(): TestMySingleton {
if (single == null)
single = TestMySingleton()
return single
}
}
}
Solution 6 - Android
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 7 - Android
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 8 - Android
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 9 - Android
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 10 - Android
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"