Kotlin singleton Implementation


#1

Recent days, I wrote and decompiled a Kotlin object, such as one below:

object Test {}

And it turns out to be something like this:

public final class Test {
   public static final Test INSTANCE;

   private Test() {
      INSTANCE = (Test)this;
   }

   static {
      new Test();
   }
}

It seems that it is similar to the Eager Implementation of Java Singleton.

The singleton instance will initialized when the class is loaded, not the class is used.

So, is there any reason why the Kotlin use the Eager Implementation not the Static-Holder Implementation which being widely used, or the Enum Implementation which described at Effective Java 2nd Edition as the Best Practice of Singleton?


#2

The singleton instance will initialized when the class is loaded, not the class is used

Isn’t the class loaded when it’s first used. I know several events when JVM loads a class, they include instantiation via construction, access to static fields and invocation of static methods. In case of Kotlin object, we get class loaded (and therefore object instantiated) at the point where someone gets INSTANCE field from Java, or just takes instance of object in Kotlin.


#3

I search a long time for when the class get loaded by JVM.

And it seems that the timing of class loaded is depended on per-JVM implementation.

In the Oracle JVM, it is indeed the class will get loaded when it first used.

But in the other JVM, it might not.

Please correct me if there is anything wrong.


#4

The JVM specification states clearly when the class gets initialized. Other JVM implementations can’t implement different behaviour, since, unlike C++, there no such thing like “undefined behaviour” in Java and JVM.


#5

The statement mentions that if you use certain method of Java Reflection, it will cause the class to initialize.

There is a typical case that I use a classpath scanner to scan for some classes with custom runtime annotation.

And it will cause the class get loaded and initialized.

Since you have to obtain a Class object to check whether there is a annotation


#6

Not every usage of reflection causes class initialization. AFAIK, only Class.forName does, and only if you pass specific value for a specific parameter. Classpath scanners not necessarily use reflection, actually they are trying to detect jars and folders which constitute classpath and scan these folders and jars for .class files, since Java does not provide API to enumerate all classes.


#7

After a long time for searching and debating with others, it turns out your guys are indeed correct.

The singleton implementation in Kotlin is actually lazy loaded, and it is guaranteed by the JVM Specification.

Thanks for correcting my misunderstanding of class loading and providing me a such great knowledge!


#8

@Alexey.Andreev
Sorry for noise, but I check a Stack Overflow Answer, please check that out.

it shows a typical code snippet:

class Logger {     
   private static Logger instance = new Logger(); 
   public static String LOG_LINE_SEPERATOR =  
      System.getProperty("line.separator");
   public static Logger getInstance() {  
          return instance;     
   } 

   public static String logPattern() {
       return null;
   }
} 
Logger.LOG_LINE_SEPERATOR; // load Logger instance or
Logger.logPattern(); // load Logger instance

It seems Kotlin still need to face that issue of Singleton, or that issue just doesn’t exist in Kotlin world?


#9

Kotlin have “no” static properties :stuck_out_tongue:
And I don’t think using LOG_LINE_SEPERATOR while not using instance a reasonable design of Logger.


#10

NO, it is not the “static or not” problem.

The Logger class in Kotlin using object will just like this:

object Logger {
    val LOG_LINE_SEPERATOR = System.getProperty("line.separator")
}

The LOG_LINE_SEPERATOR is referenced by the INSTANCE in usage.

In my opinion, it is just the almost the same with the previous Logger class.

The difference is that Java do it with a implicitly way, and Kotlin do it with a explicitly way.

Someone will just complain:

I only need the TAG of the Logger which the TAG is loaded elsewhere, not the whole instance of Logger