Java singletons are familiar with several ways of writing, but when it comes to KT, it seems strange to use Java to write. Some of them can be easily completed with the help of KT’s convention.

Several common singleton patterns

  1. Hungry (create as soon as you come, whether you really need it or not)
  2. Lazy (lazy loading, created only when used, and we’ll talk about double Check directly here)
  3. Static inner classes (synchronization with virtual machine, lazy initialization with inner class mechanism)

Two, kotlin these ways of writing

1. The hungry

Instead of using Java’s private Constroctor and getInstance interfaces, you can use Object directly.

object HungrySin {
    fun calculate() {}}Copy the code

Decompilation into Java code is the familiar Java writing method

public final class HungrySin {
   public static final HungrySin INSTANCE;

   public final void calculate() {
   }

   private HungrySin() {
   }

   static {
      HungrySin var0 = new HungrySin();
      INSTANCE = var0;
   }
}
Copy the code

2. DoubleCheck

When it comes to lazy loading, let’s jump directly to doubleCheck, which is thread safe and has good performance. If implemented in Java, there will be two layers of check. The first layer checks to reduce the burden of lock and directly determines whether the lock has been created. The second judgment is that locking ensures thread safety, and the last is that volatile disables reordering to prevent compiler optimizations from causing thread safety problems.

Kotlin doesn’t have to be that complicated, just use the BY Lazy proxy

class DoubleCheckSin private constructor() {
    companion object {
        val doubleCheckSin: DoubleCheckSin by lazy {
            DoubleCheckSin()
        }
    }

    fun calculate() {}}Copy the code

Thanks to delegates and lazy.kt

@kotlin.internal.InlineOnly
public inline operator fun <T> Lazy<T>.getValue(thisRef: Any?, property: KProperty<*>): T = value

Copy the code

As you can see above, val doubleCheckSin is delegated to lazy, and get returns the value of lazy.kt

Focus on lazy

private class SynchronizedLazyImpl<out T>(initializer: () -> T, lock: Any? = null) : Lazy<T>, Serializable { private var initializer: (() -> T)? Initializer //0._value Disable instruction reordering with volatile @volatile private var _value: Any? = UNINITIALIZED_VALUE // final field is required toenablesafe publication of constructed instance private val lock = lock ? Override val value: Tget() {val _v1 = _value //2if(_v1 ! == UNINITIALIZED_VALUE) { @Suppress("UNCHECKED_CAST")
                return _v1 as T
            }
            
            returnsynchronized(lock) { val _v2 = _value //3. The second judgment, adding synchronized key frame to ensure thread safetyif(_v2 ! == UNINITIALIZED_VALUE) { @Suppress("UNCHECKED_CAST") (_v2 as T)
                } elseConstructor val typedValue = initializer!! () _value = typedValue initializer = null typedValue } } } override fun isInitialized(): Boolean = _value ! == UNINITIALIZED_VALUE override fun toString(): String =if (isInitialized()) value.toString() else "Lazy value not initialized yet."

    private fun writeReplace(): Any = InitializedLazyImpl(value)
}
Copy the code

The above code has been commented in place

  1. _value Disables instruction reordering with volatile
  2. This is the value of the singleton delegate analyzed above
  3. If already initialized, it returns the same level 1 judgment as in Java
  4. The second judgment, adding synchronized key frame to ensure thread safety
  5. The lambda singleton that was first created and passed in with lazy is the constructor of the value

This shows that the SYNCHRONIZED mode implementation of lazy is exactly the same as Java (except for delegates and lambda functions), but using Koltin by Lazy to implement a doubelCheck singleton saves a lot of code

For By Layz, it’s more about lazy loading of views, just like singletons. Lambda is initialized on the first access, and then the previously created object is fetched directly.

Static inner classes

There is no trick in this, it is still necessary to declare a static internal class, like Java, through the virtual machine static internal class loading mechanism for thread safety, lazy loading.

class InnerSin private constructor() {
    companion object {
        val instance = Holder.holder
    }


    private object Holder {
        val holder = InnerSin()
    }


    fun calculate() {}}Copy the code

However, this is a clever way to make use of the cinit method feature of the loading class to ensure that the InnerSin object created when loading the static holder is thread-safe and has only one instance. The outer class is then loaded. Instead of loading the inner class immediately, it is loaded when the outer class calls the inner class, ensuring lazy loading.