java的單例模式幾種寫法都已經很熟悉了,但轉到kt時若是用java寫法實現倒顯得怪異了,有的能夠藉助kt的約定輕鬆完成。java
1.餓漢式安全
這種方式無需使用java的private constroctor,getInstance這些接口,直接使用object就可實現。bash
object HungrySin {
fun calculate() {
}
}
複製代碼
反編譯成java代碼,就是熟悉的java寫法ide
public final class HungrySin {
public static final HungrySin INSTANCE;
public final void calculate() {
}
private HungrySin() {
}
static {
HungrySin var0 = new HungrySin();
INSTANCE = var0;
}
}
複製代碼
2.懶漢式 doubleCheck函數
說到延遲加載就直接跳到線程安全且性能較好的doubleCheck吧,如果用java實現會用2層check,第一層判斷減輕鎖的負擔直接判斷是否建立過,第二層判斷加鎖保證線程安全,最後用volatile禁止重排序防止編譯器優化致使的線程安全問題。性能
在kotlin裏面也無需這麼複雜,直接使用by lazy代理便可實現優化
class DoubleCheckSin private constructor() {
companion object {
val doubleCheckSin: DoubleCheckSin by lazy {
DoubleCheckSin()
}
}
fun calculate() {
}
}
複製代碼
這個要歸功於委託和lazy.ktui
@kotlin.internal.InlineOnly
public inline operator fun <T> Lazy<T>.getValue(thisRef: Any?, property: KProperty<*>): T = value
複製代碼
能夠看到上文,把val doubleCheckSin委託給了lazy, get時返回的是lazy.kt的valuethis
重點來看看lazyspa
layz支持傳LazyThreadSafetyMode,有三種可選,默認的是SYNCHRONIZED,也就是線程安全,下面分析一下線程安全的具體實現。private class SynchronizedLazyImpl<out T>(initializer: () -> T, lock: Any? = null) : Lazy<T>, Serializable {
private var initializer: (() -> T)? = initializer
//0._value用volatile禁止指令重排序
@Volatile private var _value: Any? = UNINITIALIZED_VALUE
// final field is required to enable safe publication of constructed instance
private val lock = lock ?: this
//1.這個是上面分析的單例委託的value
override val value: T
get() {
val _v1 = _value
//2.若已經初始化,則直接返回 同java中的第一層判斷
if (_v1 !== UNINITIALIZED_VALUE) {
@Suppress("UNCHECKED_CAST")
return _v1 as T
}
return synchronized(lock) {
val _v2 = _value
//3.第二層判斷,加synchronized關鍵幀 保證線程安全
if (_v2 !== UNINITIALIZED_VALUE) {
@Suppress("UNCHECKED_CAST") (_v2 as T)
} else {
//4.第一次建立,把執行lazy下傳進來的lambda 單例這裏也就是值的構造構造函數
val typedValue = initializer!!()
_value = typedValue
initializer = null
typedValue
}
}
}
override fun isInitialized(): Boolean = _value !== UNINITIALIZED_VALUE
override fun toString(): String = if (isInitialized()) value.toString() else "Lazy value not initialized yet."
private fun writeReplace(): Any = InitializedLazyImpl(value)
}
複製代碼
上面的代碼對應的地方加了註釋
由此能夠看出lazy的SYNCHRONIZED模式實現和java是徹底同樣的(除了委託和lambda函數),可是使用koltin by lazy實現doubelCheck單例就就能夠少寫不少代碼
對於by layz其實用的更多的地方是view的延遲加載,道理和單例同樣的。第一次訪問的時候才執行lambda來初始化,後面就直接取以前建立的對象。
3.靜態內部類
這個其實沒有取巧的地方了,仍是得像java同樣要聲明一個靜態內部類,經過虛擬機的靜態內部類加載機制實現線程安全、延遲加載。
class InnerSin private constructor() {
companion object {
val instance = Holder.holder
}
private object Holder {
val holder = InnerSin()
}
fun calculate() {
}
}
複製代碼
不過這種方式很巧妙,利用加載類的cinit方法特行,保證加載靜態類holder時建立InnerSin對象是線程安全且只會有一個實例。而後加載外部類加載不會立刻加載內部類,等到外部類調用內部類的時候才加載,保證了延遲加載。