The one and only object
Kotlin Vocabulary
In Java, the static
keyword is used to denote methods and properties that belong to an object, not to an instance of an object. The static
keyword is also used to create Singletons, one of the most widely used design patterns. Singletons help you to create a single instance of an object, which can be accessed and shared by other objects.
Kotlin has a more elegant way to deal with this. You can use a single keyword: object
, to implement the Singleton pattern. Read on to find out the differences between implementing a Singleton in Java vs Kotlin, how you can create Singletons in Kotlin without using the static
keyword (spoiler this is achieved by using the object
keyword), and to find out what’s happening under the hood when you’re using object
.
First, let’s back up a bit and find out why we need a single instance of an object, aka Singleton.
What is a Singleton?
Singleton is a design pattern which ensures that a class has only one instance and provides a global point of access to the object. The Singleton pattern is particularly useful for objects which need to be shared between different parts in your app and for resources that are expensive to create.
Singleton in Java
To guarantee that a class has only one instance, you need to control how the object is created. To create a class with only one instance, make the constructor private and create a publicly accessible static reference of the object. While doing this, you don’t really want to create the Singleton at startup since Singletons are used for objects which are expensive to create. To achieve this, provide a static method which checks if the object is created. The method must return the previously created instance or call the constructor and return the instance.
The above code seems fine, but there is a major issue. This code is not thread-safe. At any time one thread can pass this if check but be put on hold while another thread creates the singleton. When the first thread resumes inside the if
block, it creates another instance.
To fix the threading issue, you can use double checked locking. With double checked locking, if the instance is null
, synchronized
keyword creates a lock and a second check ensures the instance is still null
. If the instance is null
, then creates the Singleton. Yet, this is not enough and the instance also needs to be marked volatile
. Volatile keyword tells the compiler that a variable might be modified asynchronously by concurrently running threads.
All of this leads to a lot of boilerplate that you need to repeat each time you need a singleton. Since this code is too complicated for such a simple task, enums are used for creating singletons in Java most of the time.
Singleton in Kotlin
Now, let’s take a look at Kotlin. Kotlin doesn’t have static methods or fields so how can we create a Singleton in Kotlin?
Actually, Android Studio/IntelliJ can help us to understand. When you convert the Singleton code in Java to Kotlin, all static properties and methods are moved to a companion object
.
The converted code works as expected but we can make it simpler. To simplify the code, remove the constructor and companion
keyword from object
. The differences between object
and companion objects
are covered later in this post.
When you want to use the count()
method, you can access it over the Singleton object. In Kotlin, object
is a special class that only has one instance. If you create a class with the object
keyword instead of class
, the Kotlin compiler makes the constructor private, creates a static reference for the object, and initializes the reference in a static block.
Static blocks are called only once when the static field is first accessed. The JVM handles the static blocks in a similar way to synchronized blocks, even though they don’t have the synchronized
keyword. When this Singleton class is initializing, the JVM acquires a lock on the synchronized block, making it impossible for another thread to access it. When the lock is released, the Singleton instance is already created so the static block won’t execute again. This guarantees that there is only one instance of the Singleton, which fulfills the Singleton contract. Plus, the object is both thread-safe and lazily created the first time it is accessed. Voila!
Let’s take a look at decompiled Kotlin byte code to understand what’s happening under the hood.
To check the byte code of a Kotlin class, select Tools > Kotlin > Show Kotlin Bytecode. Once Kotlin byte code is displayed, click Decompile to reveal the decompiled Java code.
However, object
comes with a limitation. object declarations can not have constructors which means they can not take parameters. Even if they did, it would be impossible to pass a parameter since the non-static parameter passed in the constructor isn’t accessible from the static block.
⚠️The static initialization blocks, just like other static methods, can only access static properties of a class. Static blocks are called before the constructors so there is no way they can access properties of an object or parameters passed in the constructor.
companion object
companion object
is similar to object
. companion object
is always declared in a class and their properties can be accessed by using the host object. The companion object
doesn’t require a name. If the companion object
has a name, the caller can access the members using the companion object
’s name.
For example, here we have the similar companion objects with and without a name. Any caller can access the count()
method on SomeClass
, just like it is a static member of SomeClass
. Alternatively any caller can access the count()
method by using Counter
just like a static member of AnotherClass
.
The companion object decompiles into an inner class with a private constructor. The host class initializes the inner class through a synthetic constructor, which only it can access. The host class keeps a public reference to the companion object which is accessible from other classes.
Object Expressions
So far we’ve seen the object
keyword used in object declarations. object
keyword can be used in object expressions as well. When used as an expression, object
keyword helps you to create anonymous objects and anonymous inner classes.
Let’s say you need a temporary object to hold some values. You can declare and initialize your object with the desired values on the spot and access them later.
In the generated code, this translates into an anonymous Java class, marked by <undefinedtype>
, to store the anonymous object with generated getters and setters.
The object
keyword also helps you to create anonymous classes without writing any boilerplate code. You can use an object expression and the Kotlin compiler generates the wrapper class declaration to create an anonymous class.
The object
keyword helps you create thread-safe singletons, anonymous objects and anonymous classes with less code. With object
and companion object
, Kotlin generates all the code to achieve the same functionality offered by static
keyword. Plus, you can use object expressions to create anonymous objects and classes without any boilerplate code.