kotlin經(jīng)典單例實(shí)現(xiàn)
我們都知道 Kotlin 為我們實(shí)現(xiàn)單例提供了很方便的實(shí)現(xiàn),一個(gè)關(guān)鍵詞就可以搞定:那就是 object
object SomeSingleton
反編譯成 Java 代碼:
public final class SomeSingleton {
public static final SomeSingleton INSTANCE;
private SomeSingleton() {
INSTANCE = (SomeSingleton)this;
}
static {
new SomeSingleton();
}
}
可以看出,是通過(guò)靜態(tài)內(nèi)部類實(shí)現(xiàn)的。它是《java并發(fā)編程實(shí)踐》推薦的實(shí)現(xiàn)單例的方式。因?yàn)檫@種方式不僅能夠保證單例對(duì)象的唯一性,同時(shí)也延遲了單例的實(shí)例化。
關(guān)于 java 的幾種單例設(shè)計(jì)模式實(shí)現(xiàn)方法,可以參考筆者之前寫的一篇博客:
帶參優(yōu)雅實(shí)現(xiàn)
自動(dòng)化在帶來(lái)快捷便利的同時(shí),就意味著失去一定的靈活性。
object 方式的實(shí)現(xiàn)帶來(lái)的一個(gè)局限就是不能自由傳參。因?yàn)?Kotlin 的 object 關(guān)鍵字不允許存在任何構(gòu)造函數(shù)。
或許你會(huì)想到可以通過(guò)注入的方式去實(shí)現(xiàn),但是這樣還是太麻煩,如果忘記去調(diào)用這個(gè)方法就會(huì)出問(wèn)題,相信他人也不太喜歡這樣的方式去獲取你寫的單例對(duì)象。
有沒(méi)有更為優(yōu)雅的方式實(shí)現(xiàn)呢?
當(dāng)然是有的。
我們需要參考 Kotlin 標(biāo)準(zhǔn)庫(kù)中的 lazy() 函數(shù)的實(shí)現(xiàn)思路。
把創(chuàng)建和初始化帶有參數(shù)的單例的邏輯封裝起來(lái)。并通過(guò)雙重檢查鎖定算法實(shí)現(xiàn)邏輯的線程安全。
open class SingletonHolder<out T, in A>(creator: (A) -> T) {
private var creator: ((A) -> T)? = creator
@Volatile
private var instance: T? = null
fun getInstance(arg: A): T {
val i = instance
if (i != null) {
return i
}
return synchronized(this) {
val i2 = instance
if (i2 != null) {
i2
} else {
val created = creator!!(arg)
instance = created
creator = null
created
}
}
}
//對(duì)上述方法的一種更簡(jiǎn)潔的寫法
fun getInstance2(arg: A): T =
instance ?: synchronized(this) {
instance ?: creator!!(arg).apply {
instance = this
}
}
}
這個(gè)類一擼完,它就像一個(gè)爸爸的存在。有了它接下來(lái)我們實(shí)現(xiàn)單例就變得異常簡(jiǎn)單,舉個(gè)栗子
我想實(shí)現(xiàn)一個(gè)帶 context 參數(shù)的 SkinManager 單例
class SkinManager private constructor(context: Context) {
companion object : SingletonHolder<SkinManager, Context>(::SkinManager)
}
使用方式:
SkinManager.getInstance(context)
好了,游戲結(jié)束,就這么簡(jiǎn)單~