Singleton with parameter in Kotlin

后端 未结 11 1104
无人及你
无人及你 2020-11-30 21:51

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

相关标签:
11条回答
  • 2020-11-30 22:13

    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)
    
    0 讨论(0)
  • 2020-11-30 22:14

    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)
    
    0 讨论(0)
  • 2020-11-30 22:16

    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)
    }
    
    0 讨论(0)
  • 2020-11-30 22:18
    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
            }
        }
    
    }
    
    0 讨论(0)
  • 2020-11-30 22:21

    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 inited
    
    myProxy.initOnce { "Hello World" }
    println(myValue) // OK: output Hello World
    
    myProxy.initOnce { "Never changed" } // no effect
    println(myValue) // OK: output Hello World
    
    0 讨论(0)
  • 2020-11-30 22:22

    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

    0 讨论(0)
提交回复
热议问题