35

In java we can write thead-safe singletons using double Checked Locking & volatile:

    public class Singleton {
        private static volatile Singleton instance;

        public static Singleton getInstance(String arg) {
        Singleton localInstance = instance;
        if (localInstance == null) {
            synchronized (Singleton.class) {
                localInstance = instance;
                if (localInstance == null) {
                    instance = localInstance = new Singleton(arg);
                }
            }
        }
        return localInstance;
    }
}

How we can write it in kotlin?


About object

object A {
    object B {}
    object C {}
    init {
        C.hashCode()
    }
}

I used kotlin decompiler to get that

public final class A {
   public static final A INSTANCE;

   private A() {
      INSTANCE = (A)this;
      A.C.INSTANCE.hashCode();
   }
   static {
      new A();
   }

   public static final class B {
      public static final A.B INSTANCE;
      private B() {
         INSTANCE = (A.B)this;
      }
      static {
         new A.B();
      }
   }

   public static final class C {
      public static final A.C INSTANCE;
      private C() {
         INSTANCE = (A.C)this;
      }
      static {
         new A.C();
      }
   }
}

All of object have constructor invoke in static block. Based on it, we can think that it's not lazy.

Сlose to the right answer.

    class Singleton {
        companion object {
            val instance: Singleton by lazy(LazyThreadSafetyMode.PUBLICATION) { Singleton() }
        }
    }

Decompiled:

public static final class Companion {
      // $FF: synthetic field
      private static final KProperty[] $$delegatedProperties = new KProperty[]{(KProperty)Reflection.property1(new PropertyReference1Impl(Reflection.getOrCreateKotlinClass(Singleton.Companion.class), "instance", "getInstance()Lru/example/project/tech/Singleton;"))};

      @NotNull
      public final Singleton getInstance() {
         Lazy var1 = Singleton.instance$delegate;
         KProperty var3 = $$delegatedProperties[0];
         return (Singleton)var1.getValue();
      }

      private Companion() {
      }

      // $FF: synthetic method
      public Companion(DefaultConstructorMarker $constructor_marker) {
         this();
      }
   }

I hope Kotlin developers will make non reflection implementation in future...

Magnus
  • 1,483
  • 11
  • 14
punksta
  • 2,738
  • 3
  • 23
  • 41
  • The preferred way in Java should be this: http://stackoverflow.com/a/17800038/3679676 and not double checked locking with volatile. Also see: https://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom#Example_Java_Implementation – Jayson Minard Feb 25 '16 at 15:40
  • Complexity of double lock check, and updated versions: https://en.wikipedia.org/wiki/Double-checked_locking – Jayson Minard Feb 25 '16 at 15:57
  • This is NOT the correct way for Singletons in Java! – Zordid Oct 08 '19 at 08:53
  • @Zordid could you explain? – punksta Oct 08 '19 at 09:04
  • Just google your double checked locking and you will find it to be broken. E.g. here https://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html – Zordid Oct 22 '19 at 11:11
  • 1
    tl;dr: it is broken in java prior to JDK5. who cares about that dinosaur? – guai Dec 29 '22 at 10:01

3 Answers3

35

Kotlin has an equivalent of your Java code, but more safe. Your double lock check is not recommended even for Java. In Java you should use an inner class on the static which is also explained in Initialization-on-demand holder idiom.

But that's Java. In Kotlin, simply use an object (and optionally a lazy delegate):

object Singletons {
    val something: OfMyType by lazy() { ... }

    val somethingLazyButLessSo: OtherType = OtherType()
    val moreLazies: FancyType by lazy() { ... }
}

You can then access any member variable:

// Singletons is lazy instantiated now, then something is lazy instantiated after.  
val thing = Singletons.something // This is Doubly Lazy!

// this one is already loaded due to previous line
val eager = Singletons.somethingLazyButLessSo

// and Singletons.moreLazies isn't loaded yet until first access...

Kotlin intentionally avoids the confusion people have with singletons in Java. And avoids the "wrong versions" of this pattern -- of which there are many. It instead provides the simpler and the safest form of singletons.

Given the use of lazy(), if you have other members each would individually be lazy. And since they are initialized in the lambda passed to lazy() you can do things that you were asking about for about customizing the constructor, and for each member property.

As a result you have lazy loading of Singletons object (on first access of instance), and then lazier loading of something (on first access of member), and complete flexibility in object construction.

See also:

As a side note, look at object registry type libraries for Kotlin that are similar to dependency injection, giving you singletons with injection options:

Jayson Minard
  • 84,842
  • 38
  • 184
  • 227
  • 11
    Actually, lazy() uses double checked locking with volatile internally. Also, you can't pass init arguments to objects so there is a real need for an alternative solution. – BladeCoder Jul 24 '17 at 14:28
  • 2
    Object declaration's initialization is thread-safe and done at first access. You don't need this. Just using object works for what you are asking. https://kotlinlang.org/docs/reference/object-declarations.html#object-declarations – Damia Fuentes Apr 10 '20 at 13:31
15

Object declaration is exactly for this purpose:

object Singleton {
    //singleton members
}

It is lazy and thread-safe, it initializes upon first call, much as Java's static initializers.

You can declare an object at top level or inside a class or another object.

For more info about working with objects from Java, please refer to this answer.


As to the parameter, if you want to achieve exactly the same semantics (first call to getInstance takes its argument to initialize the singleton, following calls just return the instance, dropping the arguments), I would suggest this construct:
private object SingletonInit { //invisible outside the file
    lateinit var arg0: String
}

object Singleton {
    val arg0: String = SingletonInit.arg0
}

fun Singleton(arg0: String): Singleton { //mimic a constructor, if you want
    synchronized(SingletonInit) {
        SingletonInit.arg0 = arg0
        return Singleton
    }
}

The main flaw of this solution is that it requires the singleton to be defined in a separate file to hide the object SingletonInit, and you cannot reference Singleton directly until it's initialized.

Also, see a similar question about providing arguments to a singleton.

Community
  • 1
  • 1
hotkey
  • 140,743
  • 39
  • 371
  • 326
  • it's not lazy, also object does not have constructors, so they does not have dynamic setted arguments – punksta Feb 23 '16 at 22:56
  • 1
    @StasShakirov, `object`s are initialized exactly before the first program statement they're referenced in, much as Java static members. What do you mean by 'lazy', then? As to the arguments, something can be found here: http://stackoverflow.com/questions/33128024/singleton-with-argument-in-kotlin (updated the answer). – hotkey Feb 23 '16 at 23:06
14

I recently wrote an article on that topic. TL;DR Here's the solution I came up to:

1) Create a SingletonHolder class. You only have to write it once:

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!!(arg)
                instance = created
                creator = null
                created
            }
        }
    }
}

2) Use it like this in your singletons:

class MySingleton private constructor(arg: ArgumentType) {
    init {
        // Init using argument
    }

    companion object : SingletonHolder<MySingleton, ArgumentType>(::MySingleton)
}

The singleton initialization will be lazy and thread-safe.

BladeCoder
  • 12,779
  • 3
  • 59
  • 51
  • why not `open class SingletonHolder(private val creator: (A) -> T)` – punksta Aug 28 '17 at 18:37
  • 7
    It's just an optimization to free some memory by setting the creator to null after initialization is complete. I took it from the source code of lazy(). – BladeCoder Aug 28 '17 at 19:09