Singleton class in Kotlin

2020-02-26 03:36发布

问题:

I want to know way to create singleton class, so that my Util class instantiate only once per app. However when I converted my Java class to kotlin, below code 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 parameter, and I am not getting it convert without params.

回答1:

Just

companion object {
    val instance = UtilProject()
} 

will do the job because the companion object itself is a language-level singleton.
(The instance will be assigned when the companion object is first called.)

-- Updated --

If you need to adjust when the singleton object should be initilized, 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 being initialized before when 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 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).



回答2:

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


回答3:

Super simple lazy example:

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


回答4:

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


回答5:

In Kotlin you should get rid of the whole notion of the utility singleton class. The idiomatic way is to simply move all declarations to the top level.

Java:

public final class Util {
    public static final Util UTIL = new Util();

    private int prefixLength = 4;

    private Util() {}

    public void setPrefixLength(int newLen) {
        prefixLength = newLen;
    }

    public String extractVin(String input) {
        return input.substring(prefixLength);
    }
}

Usage:

String vin = UTIL.extractVin("aoeuVN14134230430")

In Kotlin just create a separate file called util.kt with the following:

var prefixLength = 4

fun String.extractVin() = this.substring(prefixLength)

Usage:

val vin = "aoeuVN14134230430".extractVin()

But... you're polluting the top-level namespace!

If your Java intuition triggers a red flag here, simply remember that the package is the namespacing construct and as opposed to Java, Kotlin doesn't conflate the concerns of namespacing and encapsulation. There's no "package-private" access level so you're free from the burden of deciding that something must stay within the same package so it can be made package-private.

So, where in Java you create a degenerate class as a workaround, in Kotlin you just create a file in its own package.



回答6:

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


回答7:

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

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

}