Kotlin classical singleton implementation

We all know that Kotlin provides us with a handy implementation for singletons that can be done with a single keyword: Object

object SomeSingleton
Copy the code

Decompiling into Java code:

public final class SomeSingleton {
   public static final SomeSingleton INSTANCE;

   private SomeSingleton() { INSTANCE = (SomeSingleton)this; } static { new SomeSingleton(); }}Copy the code

As you can see, this is done through static inner classes. It is the recommended way to implement singletons in Java Concurrent Programming Practices. This method not only ensures the uniqueness of singletons, but also delays the instantiation of singletons.

For details on how to implement the Java singleton design pattern, please refer to my previous blog post:

Singletons of design patterns

Elegant implementation with parameters

While automation brings speed and convenience, it also means losing some flexibility.

One limitation of object implementations is that they cannot pass parameters freely. Because Kotlin’s object keyword does not allow any constructors.

You might think you could use injection to do this, but it’s still too much trouble if you forget to call this method, and others won’t like this way of getting your singleton.

Is there a more elegant way to do it?

Of course there is.

We need to refer to the lazy() function in the Kotlin library.

Encapsulate the logic for creating and initializing singletons with arguments. The thread safety of logic is realized by double-checked locking algorithm.

open class SingletonHolder<out T, in A>(creator: (A) -> T) {

    private var creator: ((A) -> T)? = creator
    @Volatile
    private var instance: T? = null

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

        return synchronized(this) {
            val i2 = instance
            if(i2 ! = null) { i2 }else{ val created = creator!! Fun getInstance2(arg: A): T = instance? (arg: A): T = instance? : synchronized(this) { instance ? : creator!! (arg).apply { instance = this } } }Copy the code

As soon as this class is finished, it’s like a father presence. With that, it becomes incredibly easy to implement singletons, for example

I want to implement a SkinManager singleton with the context parameter

class SkinManager private constructor(context: Context) {
    companion object : SingletonHolder<SkinManager, Context>(::SkinManager)
}
Copy the code

Usage:

SkinManager.getInstance(context)
Copy the code

All right, game over, simple as that