107

I am trying to convert an Android app from Java to Kotlin. There are a few singletons in the app. I used a companion object for the singletons without constructor parameters. There is another singleton that takes a constructor parameter.

Java code:

public class TasksLocalDataSource implements TasksDataSource {

    private static TasksLocalDataSource INSTANCE;

    private TasksDbHelper mDbHelper;

    // Prevent direct instantiation.
    private TasksLocalDataSource(@NonNull Context context) {
        checkNotNull(context);
        mDbHelper = new TasksDbHelper(context);
    }

    public static TasksLocalDataSource getInstance(@NonNull Context context) {
        if (INSTANCE == null) {
            INSTANCE = new TasksLocalDataSource(context);
        }
        return INSTANCE;
    }
}

My solution in kotlin:

class TasksLocalDataSource private constructor(context: Context) : TasksDataSource {

    private val mDbHelper: TasksDbHelper

    init {
        checkNotNull(context)
        mDbHelper = TasksDbHelper(context)
    }

    companion object {
        lateinit var INSTANCE: TasksLocalDataSource
        private val initialized = AtomicBoolean()

        fun getInstance(context: Context) : TasksLocalDataSource {
            if(initialized.getAndSet(true)) {
                INSTANCE = TasksLocalDataSource(context)
            }
            return INSTANCE
        }
    }
}

Am I missing anything? Thread safety? Laziness ?

There were a few similar questions but I don't like the answers :)

LordRaydenMK
  • 13,074
  • 5
  • 50
  • 56
  • It's a bit awkward that the `INSTANCE` property exposes public setter – miensol Nov 03 '16 at 09:47
  • @miensol any other option to pass the parameter (Context) to the companion object? – LordRaydenMK Nov 03 '16 at 09:48
  • 1
    Storing a Context instance in a global singleton object (no matter whether Java or Kotlin) creates a memory leak: http://stackoverflow.com/a/11908685/147024 – yole Nov 03 '16 at 09:59
  • 6
    @yole it is NOT a memory leak if it is the application context which is a singleton. – LordRaydenMK Nov 03 '16 at 10:00
  • 1
    @LordRaydenMK I think there are 4 improvements in your code if you use Kotlin in Android. I have made a gist with some explaination. Check it out : https://gist.github.com/gaplo917/f186d5c541fbc0d6f77f9b720ec4694c – Gary LO Nov 04 '16 at 03:52
  • @GaryLO the first 2 points are very good. Having a init method like in this answer http://stackoverflow.com/a/33129510/1011435 is what I was trying to avoid. Thanks – LordRaydenMK Nov 04 '16 at 08:12
  • @LordRaydenMK Why you don't like to use `Singleton.init()`? Most of the Android libraries would be initialized once in the `Application.onCreate()` and then you can use it everywhere. Also, if you are working in Android, you can try dagger 2. Most of the time you no longer need to define your own singleton. – Gary LO Nov 04 '16 at 10:00

14 Answers14

157

Here's a neat alternative from Google's architecture components sample code, which uses the also function:

class UsersDatabase : RoomDatabase() {

    companion object {

        @Volatile private var INSTANCE: UsersDatabase? = null

        fun getInstance(context: Context): UsersDatabase =
            INSTANCE ?: synchronized(this) {
                INSTANCE ?: buildDatabase(context).also { INSTANCE = it }
            }

        private fun buildDatabase(context: Context) =
            Room.databaseBuilder(context.applicationContext,
                    UsersDatabase::class.java, "Sample.db")
                    .build()
    }
}
BakaWaii
  • 6,732
  • 4
  • 29
  • 41
fal
  • 2,997
  • 1
  • 22
  • 13
  • 3
    I am not sure why there is INSTANCE ?: inside synchronized block? Because that block will be called only when INSTANCE was null so why to check INSTANCE was null or not again there? Can someone explain? – Sandip Fichadiya Jun 18 '18 at 06:51
  • 10
    @SandipSoni Read about double-check locking: https://stackoverflow.com/questions/18093735/double-checked-locking-in-singleton – kuhnroyal Jun 23 '18 at 11:53
  • Correct, the key here is not to allow the class to instantiate itself through the constructor so as not to go ahead and set everything up inside without an initialized reference of the appropriate objects, ending up in lateinit property XYZ not initialized – Odaym Nov 17 '18 at 23:22
  • 4
    I see two problems with this code. 1. You always have to pass context (or any other var that is defined) which may be not available at the time where you want to retrieve the Singleton 2. Assuming you pass a different context on second use. What is the meaning of that? You will simply get the old singleton. Seems like an anti-pattern to me. – A1m Feb 15 '20 at 03:30
  • 3
    @A1m With regards to 2): passing a different context the second time would not matter, since the application context is used to construct the database. The `Context` object you pass is only used to retrieve the application context. – mfb Mar 28 '20 at 15:31
  • 2
    @MathiasBrandt that may be true in this case speaking about `Context` that should itself be a singleton. But the question in general asks about a pattern to create this singleton. I feel that behavior is undefined and missleading. – A1m Mar 30 '20 at 09:17
  • You may also add @Jvmstatic above getInstance – Ofek Ron Aug 05 '21 at 13:04
56

Thread-Safe Solution # Write Once; Use Many;

It's a good solution to create a class implementing the logic of singleton which also holds the singleton instance, like the following.

It instantiates the instance using Double-Check Locking in a synchronized block to eliminate possibility of race condition in multi-threaded environments.

SingletonHolder.kt

open class SingletonHolder<out T, in A>(private val constructor: (A) -> T) {

    @Volatile
    private var instance: T? = null

    fun getInstance(arg: A): T =
        instance ?: synchronized(this) {
            instance ?: constructor(arg).also { instance = it }
        }
}

Usage

Now in each class that you want to be singleton, write a companion object extending the above class. SingletonHolder is a generic class that accepts type of target class and its requiring parameter as generic params. It also needs a reference to the constructor of target class which is used for instantiating an instance:

class MyManager private constructor(context: Context) {

    fun doSomething() {
        ...
    }

    companion object : SingletonHolder<MyManager, Context>(::MyManager)
}

Finally:

MyManager.getInstance(context).doSomething()
aminography
  • 21,986
  • 13
  • 70
  • 74
  • 7
    Assume that MyManger requires more than one constructor parameters, How to handle it with SingletonHolder – Abhi Muktheeswarar Sep 10 '19 at 06:09
  • @AbhiMuktheeswarar: As `SingletonHolder` is a generic class and unfortunately its generic types can't be defined dynamically at runtime, possibly for each count of input parameters we should define a class like this: https://hastebin.com/avunuwiviv.m – aminography Sep 10 '19 at 06:46
  • How to create test cases for such classes? – Jimit Patel Jan 27 '20 at 13:11
  • 2
    @AbhiMuktheeswarar use a Pair for A. Then: companion object: SingletonHolder> { MyManager(it.first,it.second) } – Dan Brough Oct 26 '20 at 23:29
  • This is neat but sadly it crashes my compiler with `CompilationException: Back-end (JVM) Internal error: Failed to generate expression: KtCallExpression` – ThanosFisherman Jan 11 '21 at 09:30
  • @ThanosFisherman: Thank you for reporting this. Would you use `kotlinx.serialization` in the singleton class? and what is your platform? android? – aminography Jan 11 '21 at 09:49
  • @aminography I tried to use `SingletonHolder` to create a singleton of my room database but it wouldn't compile. Probably Room is using `kotlinx.serialization` under the hood. so yeah Android API 30. I noticed there is an open issue about this somewhere on github. – ThanosFisherman Jan 11 '21 at 12:10
  • 1
    I really like this solution, but: (1) I was nervous about extending the companion object (what if something else wanted to extend it too?), (2) I was nervous about all the `!!`s and (3) I think it should be made more explicit when you might be creating the singleton, vs when you just want to access it. So I made my own version heavily inspired by this answer: https://gist.github.com/chriscoomber/80a9e66dd4fba92e7480d29c925440e9 – Chrispher Jan 28 '21 at 18:05
  • This is a good solution but how are you sharing this instance with Java code? – Shubham AgaRwal Sep 12 '22 at 12:28
23

I am not entirely sure why would you need such code, but here is my best shot at it:

class TasksLocalDataSource private constructor(context: Context) : TasksDataSource {
    private val mDbHelper = TasksDbHelper(context)

    companion object {
        private var instance : TasksLocalDataSource? = null

        fun  getInstance(context: Context): TasksLocalDataSource {
            if (instance == null)  // NOT thread safe!
                instance = TasksLocalDataSource(context)

            return instance!!
        }
    }
}

This is similar to what you wrote, and has the same API.

A few notes:

  • Do not use lateinit here. It has a different purpose, and a nullable variable is ideal here.

  • What does checkNotNull(context) do? context is never null here, this is guarantied by Kotlin. All checks and asserts are already implemented by the compiler.

UPDATE:

If all you need is a lazily initialised instance of class TasksLocalDataSource, then just use a bunch of lazy properties (inside an object or on the package level):

val context = ....

val dataSource by lazy {
    TasksLocalDataSource(context)
}
Community
  • 1
  • 1
voddan
  • 31,956
  • 8
  • 77
  • 87
  • Exactly what I was looking for... About the usage of this code... I am trying to convert https://github.com/googlesamples/android-architecture to Kotlin to get my feet wet with Kotlin and to compare the code. That is why I don't want to include Dagger or https://github.com/SalomonBrys/Kodein – LordRaydenMK Nov 05 '16 at 14:22
  • Out of curiosity, what file was you trying to convert? – voddan Nov 05 '16 at 14:27
  • https://github.com/googlesamples/android-architecture/blob/todo-mvp/todoapp/app/src/main/java/com/example/android/architecture/blueprints/todoapp/data/source/local/TasksLocalDataSource.java – LordRaydenMK Nov 05 '16 at 14:28
  • 1
    @LordRaydenMK IMHO, don't start learning Kotlin by doing a Java conversion. Read the official documentation once, try to do a complete equivalent Kotlin implementation starting from scratch but not doing a Java conversion. Concept of "Don't code Kotlin in Java way" is very important because most of the time you don't need Java (in)famous coding pattern (as it was designed for Java). – Gary LO Nov 05 '16 at 18:12
  • 1
    @GaryLO I did the koans on try.kotlinglang.org. As Hadi Hariri (https://twitter.com/hhariri) said in a talk on some conference (can't find the link now)... some Kotlin is better than no Kotlin for start. And the reason I wrote here is to try and do it the Kotlin way. Thanks. – LordRaydenMK Nov 05 '16 at 18:21
  • 2
    Beware that this solution is **not thread-safe**. Two singleton instances may exist and get initialized if multiple threads try to access it simultaneously. – BladeCoder Jul 23 '17 at 18:14
  • The first snippet is not thread safe (obviously), why the second one is – voddan Jul 23 '17 at 22:15
  • Would be it good practice to use `!!`? There might be some errors while creating `TasksLocalDataSource(context)` and hence `instance` can be null at a time of return. – musooff Nov 20 '18 at 02:12
  • 1
    @musooff `TasksLocalDataSource(context)` is a constructor, so it ether returns an object or throws an exception. – voddan Nov 20 '18 at 10:35
  • In the by-lazy example, it looks like the `context` instance would be retained, which might be undesirable. Maybe that should be fixed by adding `context = null` after `TasksLocalDataSource(context)` – sjjhsjjh Mar 10 '20 at 10:31
9

You can declare a Kotlin object, overloading "invoke" operator.

object TasksLocalDataSource: TasksDataSource {
    private lateinit var mDbHelper: TasksDbHelper

    operator fun invoke(context: Context): TasksLocalDataSource {
        this.mDbHelper = TasksDbHelper(context)
        return this
    }
}

Anyway I think that you should inject TasksDbHelper to TasksLocalDataSource instead of inject Context

  • This method is the most straightforward and is a good use case for the `invoke` function as a method to turn a _Class_ into a function. I used this pattern for my _Repository_ in the [_**Coinverse**_](https://play.google.com/store/apps/details?id=app.coinverse) app. – AdamHurwitz May 21 '19 at 21:20
  • 3
    But is it thread-safe? I see a lot of people praising [this](https://stackoverflow.com/a/53580852/1103974), which is actually borrowed from [this 2yo popular medium post](https://medium.com/@BladeCoder/kotlin-singletons-with-argument-194ef06edd9e), which again, borrowed from Kotlin "by lazy" source itself, as stated by the author. This answer looks cleaner (I hate adding boilerplate util files, let alone classes), but I digress about using this singleton in multi-threaded scenarios. – leRobot Jul 12 '19 at 02:51
5

if you want to pass a parameter to the singleton in an easier way I think this is better and shorter

object SingletonConfig {

private var retrofit: Retrofit? = null
private const val URL_BASE = "https://jsonplaceholder.typicode.com/"

fun Service(context: Context): Retrofit? {
    if (retrofit == null) {
        retrofit = Retrofit.Builder().baseUrl(URL_BASE)
                .addConverterFactory(GsonConverterFactory.create())
                .build()
    }
    return retrofit
}

}

and you call it in this easy way

val api = SingletonConfig.Service(this)?.create(Api::class.java)
5

The method synchronized() is marked as deprecated in the common standard library so an alternative would be this:

class MySingleton private constructor(private val param: String) {

    companion object {
        @Volatile
        private var INSTANCE: MySingleton? = null

        @Synchronized
        fun getInstance(param: String): MySingleton = INSTANCE ?: MySingleton(param).also { INSTANCE = it }
    }
}
Georgios
  • 4,764
  • 35
  • 48
2

If the only parameter you need is the application Context, then you can initialize it to a top level val, early in a ContentProvider, like the Firebase SDK does.

Since declaring a ContentProvider is a bit cumbersome, I made a library that provides a top level property named appCtx for all places where you don't need an Activity or other special lifecycle bound context.

Louis CAD
  • 10,965
  • 2
  • 39
  • 58
2

If you looking for a base SingletonHolder class with more than one argument. I had created the SingletonHolder generic class, which supports to create only one instance of the singleton class with one argument, two arguments, and three arguments.

link Github of the base class here

Non-argument (default of Kotlin):

object AppRepository 

One argument (from an example code in the above link):

class AppRepository private constructor(private val db: Database) {
    companion object : SingleArgSingletonHolder<AppRepository, Database>(::AppRepository)
}
// Use
val appRepository =  AppRepository.getInstance(db)

Two arguments:

class AppRepository private constructor(private val db: Database, private val apiService: ApiService) {
    companion object : PairArgsSingletonHolder<AppRepository, Database, ApiService>(::AppRepository)
}
// Use
val appRepository =  AppRepository.getInstance(db, apiService)

Three arguments:

class AppRepository private constructor(
   private val db: Database,
   private val apiService: ApiService,
   private val storage : Storage
) {
   companion object : TripleArgsSingletonHolder<AppRepository, Database, ApiService, Storage>(::AppRepository)
}
// Use
val appRepository =  AppRepository.getInstance(db, apiService, storage)

More than 3 arguments:

To implement this case, I suggest creating a config object to pass to the singleton constructor.

Wilson Tran
  • 4,050
  • 3
  • 22
  • 31
1

solution with lazy

class LateInitLazy<T>(private var initializer: (() -> T)? = null) {

    val lazy = lazy { checkNotNull(initializer) { "lazy not initialized" }() }

    fun initOnce(factory: () -> T) {
        initializer = factory
        lazy.value
        initializer = null
    }
}

val myProxy = LateInitLazy<String>()
val myValue by myProxy.lazy

println(myValue) // error: java.lang.IllegalStateException: lazy not initialized

myProxy.initOnce { "Hello World" }
println(myValue) // OK: output Hello World

myProxy.initOnce { "Never changed" } // no effect
println(myValue) // OK: output Hello World
William Leung
  • 1,556
  • 17
  • 26
1
class CarsRepository(private val iDummyCarsDataSource: IDummyCarsDataSource) {

    companion object {
        private var INSTANCE: CarsRepository? = null
        fun getInstance(iDummyCarsDataSource: IDummyCarsDataSource): CarsRepository {
            if (INSTANCE == null) {
                INSTANCE = CarsRepository(
                    iDummyCarsDataSource = iDummyCarsDataSource)
            }
            return INSTANCE as CarsRepository
        }
    }

}
Braian Coronel
  • 22,105
  • 4
  • 57
  • 62
1

I'm new to Kotlin development, so I wanted the simplest solution but one that also resembles Java Singleton as much as possible. Double Checking for thread safety, Private Constructor, volatile reference. Below code worked for me best. Sharing it here in case someone else needs it.

class InstrumentationManager private constructor(prodToken: String, intToken: String) {
companion object {
    @Volatile
    private var INSTANCE: InstrumentationManager? = null
    fun getInstance(prodToken: String, intToken: String): InstrumentationManager =
        INSTANCE ?: synchronized(this) {
            INSTANCE ?: InstrumentationManager(prodToken, intToken).also { INSTANCE = it }
    }
}

}

Description

  • private constructor --> private InstrumentationManager()
  • InstrumentationManager? --> @Nullable
  • INSTANCE ?: --> if(instance == null) { }
  • InstrumentationManager(prodToken, intToken).also --> Extra processing once InstrumentationManager object is created.
Arpit Ratan
  • 2,976
  • 1
  • 12
  • 20
0

I saw all the answers. I know this is a repeated answer but if we use the synchronized keyword on the method declaration, it will synchronize the whole method to the object or class. And synchronized block is not deprecated yet.

You can use the following utility class to get the singleton behavior.

open class SingletonWithContextCreator<out T : Any>(val creator: (Context) -> T) {
    @Volatile
    private var instance: T? = null

    fun with(context: Context): T = instance ?: synchronized(this) {
        instance ?: creator(context).apply { instance = this }
    }
}

You can extend the above-mentioned class whichever class you wanted to make singleton.

In your case the following is the code to make TasksLocalDataSource class singleton.

companion object : SingletonWithContextCreator<TasksDataSource>(::TasksLocalDataSource)
Prasad
  • 3,462
  • 1
  • 23
  • 28
0

This is an example of a singleton in kotlin I test it with threads and there was no exception

class ShoppingCartClassic private  constructor() {

   private var outfits: ArrayList<Outfit> = ArrayList()
   
   companion object{
       @Volatile
       private var instance: ShoppingCartClassic? = null

       fun get(): ShoppingCartClassic {
           synchronized(this) {
//                return instance?: ShoppingCartClassic()  // I commented this because I got lower performance 
               if (instance == null) {
                   instance = ShoppingCartClassic()
               }
               return this.instance!!
           }
       }
   }

   fun addOutFit(outfit: Outfit){
       outfits.add(outfit)
   }

   fun removeOutFit(outfit: Outfit){
       outfits.remove(outfit)
   }

   fun checkout() :List<Outfit>{

       return outfits
   }
}

Here is the test

companion object {
        @JvmStatic
        fun main(args: Array<String>) {
            val outfit1 = Outfit(
                pants = Pants("short pants1", Color.BLACK),
                shoes = Shoes("cool shoes1", Color.BLACK),
                shirt = Shirt("my shirt1", Color.GREEN)
            )
            val outfit2 = Outfit(
                pants = Pants("short pants2", Color.BLACK),
                shoes = Shoes("cool shoes2", Color.BLACK),
                shirt = Shirt("my shirt2", Color.BLUE)
            )
            val outfit3 = Outfit(
                pants = Pants("short pants3", Color.BLACK),
                shoes = Shoes("cool shoes3", Color.BLACK),
                shirt = Shirt("my shirt3", Color.BLACK)
            )
            val threads: ArrayList<Thread> = arrayListOf()

            for (i in 0..3) {

                val thread = Thread {
                    val instance = ShoppingCartClassic.get()
                    instance.addOutFit(outfit1)
                    instance.addOutFit(outfit2)
                    instance.addOutFit(outfit3)


                    instance.checkout().forEach {
                        println(it.shirt.style)
                    }
                }
                threads.add(thread)
            }
            threads.forEach (Thread::start)
        }
    }

and this is my result

my shirt1
my shirt1
my shirt2
my shirt3
my shirt1
my shirt2
my shirt3
my shirt1
my shirt2
my shirt3
my shirt2
my shirt3
....

**I also tested ** and I got this errors

Exception in thread "Thread-1" Exception in thread "Thread-3" java.util.ConcurrentModificationException
    at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:937)
    at java.base/java.util.ArrayList$Itr.next(ArrayList.java:891)
    at dp.sigleton.Main$Companion.main$lambda-1(Main.kt:51)
    at java.base/java.lang.Thread.run(Thread.java:844)
open class SingletonHolder<out T, in A>(private val constructor: (A) -> T) {

    @Volatile
    private var instance: T? = null

    fun getInstance(arg: A): T =
        instance ?: synchronized(this) {
            instance ?: constructor(arg).also { instance = it }

        }
}
-13
Singletons

Singletons are used often enough for a simpler way of creating them to exist. Instead of the usual static instance, getInstance() method and a private constructor, Kotlin uses the object notation. For consistency, object notation is also used to define static methods.

 object CommonApiConfig {
private var commonApiConfig: CommonApiConfig? = null
fun getInstance(): CommonApiConfig {
    if (null == commonApiConfig) {
        commonApiConfig = CommonApiConfig
       }
    return CommonApiConfig.commonApiConfig!!
   }
}
Emran Hamza
  • 3,829
  • 1
  • 24
  • 20
  • 3
    Does NOT answer the question. The question is how to create a singleton when you have to pass an argument in the constructor. – LordRaydenMK Oct 05 '17 at 16:42