33
votes

The Kotlin reference says that I can create a singleton using the object keyword like so:

object DataProviderManager {
  fun registerDataProvider(provider: DataProvider) {
    //
  }
}

However, I would like to pass an argument to that object. For example an ApplicationContext in an Android project.

Is there a way to do this?

6

6 Answers

14
votes

Since objects do not have constructors what I have done the following to inject the values on an initial setup. You can call the function whatever you want and it can be called at any time to modify the value (or reconstruct the singleton based on your needs).

object Singleton {
    private var myData: String = ""

    fun init(data: String)  {
        myData = data
    }

    fun singletonDemo() {
        System.out.println("Singleton Data: ${myData}")
    }
}
6
votes

Kotlin has a feature called Operator overloading, letting you pass arguments directly to an object.

object DataProviderManager {
  fun registerDataProvider(provider: String) {
      //
  }

  operator fun invoke(context: ApplicationContext): DataProviderManager {
      //...
      return this
  }
}

//...
val myManager: DataProviderManager = DataProviderManager(someContext)
5
votes

With most of the existing answers it's possible to access the class members without having initialized the singleton first. Here's a thread-safe sample that ensures that a single instance is created before accessing any of its members.

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 }
    }

    fun printParam() {
        print("Param: $param")    
    }
}

Usage:

MySingleton.getInstance("something").printParam()
3
votes

There are also two native Kotlin injection libraries that are quite easy to use, and have other forms of singletons including per thread, key based, etc. Not sure if is in context of your question, but here are links to both:

Typically in Android people are using a library like this, or Dagger, et al to accomplish parameterizing singletons, scoping them, etc.

1
votes

I recommend that you use this form to pass arguments in a singleton in Kotlin debit that the object your constructor is deprived and blocked:

object Singleton {

    fun instance(context: Context): Singleton {
        return this
    }

    fun SaveData() {}
}

and you call it this way in the activity

Singleton.instance(this).SaveData()
0
votes

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.