Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting a Singleton in Kotlin

Tags:

kotlin

I have been looking at some Google sample code and they seem to create a singleton using the the following code:

companion object {

        // For Singleton instantiation
        @Volatile
        private var instance: CarRepository? = null

        fun getInstance(carDao: CarDao) =
                instance ?: synchronized(this) {
                    instance ?: CarRepository(carDao).also { instance = it }
                }
    }

So I know @Volatile means that

Marks the JVM backing field of the annotated property as volatile, meaning that writes to this field are immediately made visible to other threads.

Should all Singletons instances always be marked as @Volatile? If so, why?

Lastly, I don't understand the getInstance function

instance ?: synchronized(this) {
    instance ?: CarRepository(carDao).also { instance = it }
}

What is it exactly doing here?

UPDATE: Source: Google's Sunflower I changed the Repository and Dao name for my own use, but it is the same logic in the Repository files.

like image 602
Alan Avatar asked Aug 23 '18 14:08

Alan


People also ask

Why do we use singleton in Kotlin?

By using the keyword object in your app, you're defining a singleton. A singleton is a design pattern in which a given class has only one single instance inside the entire app. A singleton's two most common use cases are: To share data between two otherwise unrelated areas of your project.

Are Kotlin objects Singletons?

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 . Contrary to a class , an object can't have any constructor, but init blocks are allowed if some initialization code is needed.

Is singleton thread-safe Kotlin?

Singleton Pattern ensures that only one instance would be created and it would act as a single point of access thereby ensuring thread safety. But the above codes are dangerous, especially if it's used in different threads. If two threads access this singleton at a time, two instances of this object could be generated.

Is a companion object a singleton?

In short, companion objects are singleton objects whose properties and functions are tied to a class but not to the instance of that class — basically like the “static” keyword in Java but with a twist.


2 Answers

There's a great answer here for why the field should be volatile. Essentially, without it, it's possible for one thread to get a reference to the instance before it has been fully constructed.

For the getInstance() function, you have:

instance ?:

This means that the method will return instance if it's not null, otherwise it will execute the right side of the ?:.

synchronized(this) {
    instance ?:
}

Similarly here, after the first check for whether or not the instance is null, after synchronizing on the class (the companion object) it again checks for a non-null value and returns it if available, before executing the last command:

CarRepository(carDao).also { instance = it }

This initializes a new CarRepository and then using the .also block, assigns it (the CarRepository) to the instance field before returning. It's a bit confusing just because the entire statment is an expression. If you make this much more verbose it might look like:

fun getInstance(carDao: CarDao): CarRepository {
    var cachedInstance = instance
    if (cachedInstance != null) {
        return cachedInstance
    }

    synchronized(this) {
        cachedInstance = instance
        if (cachedInstance == null) {
            cachedInstance = CarRepository(carDao)
            instance = cachedInstance
        }
        return cachedInstance
    }
}

As a word of warning I'm not really convinced this particular example is a good pattern to follow. For example, consider the following:

val carDao1 = CarDaoImpl1()
val carDao2 = CarDaoImpl2()

val carRepo1 = CarRepository.getInstance(carDao1)
val carRepo2 = CarRepository.getInstance(carDao2)

// carRepo2 actually points to carDao1!
like image 74
Kevin Coppock Avatar answered Oct 27 '22 06:10

Kevin Coppock


Even though this isn't a real singleton, I will try to explain what's exactly going on with comments:

  fun getInstance(carDao: CarDao) =
         /* if the instance is not null, just return it: */
            instance ?: 
         /* instance is null... enter synchronized block for the first thread... 
            all other threads entering here while the first one is still not finished will block then */
            synchronized(this) {
         /* now the next line is actually here for all the blocked threads... as soon as they are released, they should take the instance that was set by the first thread */
                instance ?: 
         /* the next line actually is only executed by the first thread entering the synchronized-block */
                CarRepository(carDao).also { 
                           /* and this sets the instance that finally is returned by all others */
                             instance = it }
            }

Regarding the @Volatile... well... that's here so that the instance variable actually gets synchronized between the threads then... so that it is available when the first thread returns and the other continue entering the synchronized-block.

Now after the explanation: for a Kotlin way to write singletons check the Kotlin reference regarding Object Expressions, Object Declarations and Companion Objects.

like image 30
Roland Avatar answered Oct 27 '22 07:10

Roland