Kotlin singletons with argument

object has its limits

In Kotlin, the singleton pattern is used as a replacement for static members and fields that don’t exist in that programming language. A singleton is created by simply declaring an object.

object SomeSingleton

Contrary to a class, an object can’t have any constructor, but init blocks are allowed if some initialization code is needed.

object SomeSingleton {
init {
println("init complete")

The object will be instantiated and its init blocks will be executed lazily upon first access, in a thread-safe way. To achieve this, a Kotlin object actually relies on a Java static initialization block. The above Kotlin object will be compiled to the following equivalent Java code:

public final class SomeSingleton {
public static final SomeSingleton INSTANCE;

private SomeSingleton() {
INSTANCE = (SomeSingleton)this;
System.out.println("init complete");

static {
new SomeSingleton();

This is the preferred way to implement singletons on a JVM because it enables thread-safe lazy initialization without having to rely on a locking algorithm like the complex double-checked locking. By simply using an object declaration in Kotlin, you are guaranteed to get a safe and efficient singleton implementation.

Image for post
Image for post
Singletons: forever alone

Passing an argument

There are valid cases for which passing an argument to a singleton initialization block is the recommended pattern. The alternative requires the singleton class to be aware of some external component to be able to retrieve that argument, violating the principle of separation of concerns and making the code less reusable. To mitigate the issue, that external component could be a dependency injection system. It’s a valid solution but you don’t always want to use that kind of library and there are some cases where it can’t be used, like we’ll see in the following Android example.

Another scenario where you have to rely on a different way to manage a singleton in Kotlin is when the concrete implementation of the singleton is generated by an external tool or library (Retrofit, Room, …) and its instance is retrieved using a custom builder or factory method. In that case, you usually declare the singleton as an interface or abstract class and it can’t be an object.

An Android use case

  • Early initialization: All components are initialized by calling their public init functions at startup time in Application.onCreate() where the application Context is available, before (almost) any other code runs. This simple solution has the main downside of slowing down the application startup by blocking the main thread, initializing all components including those that are not going to be used immediately. Another less known issue is that content providers may be created before this method is called (as stated in the documentation), so if a content provider makes use of a global application component, that component must be able to be initialized before Application.onCreate() or your application may crash.
  • Lazy initialization: This is the recommended way. The component is a singleton and a function returning its instance takes a Context argument. The singleton instance will be created and initialized using this argument the first time it’s invoked. This requires some synchronization mechanism in order to be thread-safe.
    An example of standard Android component using this pattern is LocalBroadcastManager:

A reusable Kotlin implementation

open class SingletonHolder<out T: Any, in A>(creator: (A) -> T) {
private var creator: ((A) -> T)? = creator
@Volatile private var instance: T? = null

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

return synchronized(this) {
i2 = instance
(i2 != null) {
} else {
val created = creator!!(arg)
instance = created
creator = null

Note that adding the @Volatile annotation to the instance field is required for the algorithm to work properly.

This may not be the most compact or elegant Kotlin code, but it’s the one producing the most efficient bytecode for the double-checked locking algorithm. Trust the Kotlin authors on this: this code is actually borrowed directly from the implementation of the lazy() function in the Kotlin standard library, which is synchronized by default. It has been modified to allow passing an argument to the creator function.

Given its relative complexity, it’s not the kind of code you want to write (or read) more than once, so the goal is to reuse that SingletonHolder class everytime you have to implement a singleton with argument.

The logical place to declare the getInstance() function is inside the companion object of the singleton class, which allows it to be called by using simply the singleton class name as qualifier, similarly to a static method in Java. One powerful feature that Kotlin companion objects offer is their ability to inherit from a base class like any other object, which enables something comparable to static-only inheritance. In this case, we want to use SingletonHolder as a base class for the companion object of the singleton, in order to reuse and automatically expose its getInstance() function on the singleton class.

As for the creator function passed as argument to the SingletonHolder constructor, a custom lambda could be declared inline but the common solution is to pass a reference to the private constructor of the singleton class. The final code for a singleton with argument would look like this:

class Manager private constructor(context: Context) {
init {
// Init using context argument

companion object : SingletonHolder<Manager, Context>(::Manager)

Not as short as the object notation, but probably the next best thing.

The singleton may now be invoked using the following syntax and its initialization will be lazy and thread-safe:


You can also use this idiom when a singleton implementation is generated by an external library and the builder requires an argument. Here’s an example using the Room Persistence Library for Android:

@Database(entities = arrayOf(User::class), version = 1)
abstract class UsersDatabase : RoomDatabase() {

abstract fun userDao(): UserDao

companion object : SingletonHolder<UsersDatabase, Context>({
UsersDatabase::class.java, "Sample.db")

Note: When the builder doesn’t require an argument, you can simply use a lazy delegated property instead:

interface GitHubService {

companion object {
val instance: GitHubService by lazy {
retrofit = Retrofit.Builder()

I hope you found these code snippets useful. If you want to suggest other ways to solve this problem or have questions, don’t hesitate to start a discussion in the comments section. Thank you for reading!

Written by

Android developer from Belgium, blogging about advanced programming topics.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store