Java 代理、Kotlin 委託:概述
● 在 Java 語言中沒有語言層級的委託模式,但相對應的可以透過 OOP 設計的代理模式來實現,而 Java 的代理又分為兩種
● Kotlin 的特別處之一在於「語言層級的代理」,也就是該篇文章要重點說明的,它包括代理類、代理屬性
「代理,
Proxy
」這個詞也可以替換為「委託,Delegate
」,兩者個目的是相同的
Kotlin 委託類
● Kotlin 使用「by
關鍵字」來設定委託類;以下我們來 ProxyShop 代理 RealShop 類,實現與 Java 一樣的代理模式
// 1. 要有共同的接口
interface IShop {
fun buy(stuffName : String)
}
// 2. 實作類實現接口
open class RealShop : IShop{
override fun buy(stuffName : String) {
println("Real Shop buy $stuffName for you.")
}
}
// 3. 代理類實現接口,並使用 `by` 指定代理
class ProxyShop(realShop: IShop) : IShop by realShop
fun main() {
val proxy = ProxyShop(RealShop())
proxy.buy("Unit test of Kotlin")
}
委託類的模式下,可以用來「取代一般的繼承」,以上範例的功能更像是透過建構函數注入(
DI by constructor
)來完成代理
● Kotlin 委託類的另一個特點是,它也可以委託 多個類(或是說委託多個介面),範例如下
interface IShop {
fun buy(stuffName : String)
}
open class RealShop : IShop{
override fun buy(stuffName : String) {
println("Real Shop buy $stuffName for you.")
}
}
interface IPay {
fun pay(cost : Int)
}
class LinePay : IPay {
override fun pay(cost: Int) {
println("Line Pay: ${cost + 10}")
}
}
class BankPay : IPay {
override fun pay(cost: Int) {
println("Bank Pay: ${cost + 20}")
}
}
// 委託多個介面
class ProxyShop(realShop: IShop, realPay : IPay) : IShop by realShop, IPay by realPay
fun main() {
// 指定代理 RealShop, LinePay
val line = ProxyShop(RealShop(), LinePay())
line.pay(100)
line.buy("Unit test of Kotlin")
// 指定代理 RealShop, BankPay
val bank = ProxyShop(RealShop(), BankPay())
bank.pay(100)
bank.buy("Unit test of Kotlin")
}
Kotlin 委託屬性
● 一般來說我們在取得屬性時,會透過各自屬性的 setter
/getter
來取得,在這裡我們也可以用「by
關鍵字」來做委託,之後的都透過委託來達成;
Kotlin 委託屬性格式如下
val/var <property 名稱>: <Type 類型> by <代理 setter/getter 類>
● 覆寫操作符:要委託屬性就必須覆寫
getValue
(對應 get),setValue
(對應 set) 兩個操作符
● Kotlin 的屬性委託常見的有兩種寫法,範例如下
A. 基礎委託屬性使用
// 委託寄存類
class Delegate {
// 委託 getter
operator fun getValue(user: User, property: KProperty<*>): Any {
return "getValue be call -> fieldName: ${property.name}"
}
// 委託 setter
operator fun setValue(user: User, property: KProperty<*>, any: Any) {
println("setValue be call -> fieldName: ${property.name}")
}
}
class User {
// 使用委託屬性
var name by Delegate()
var password by Delegate()
}
fun main() {
val user = User()
println("${user.name}")
user.name = "Kyle"
println("${user.password}")
user.password = "123456"
}
B. 繼承 ReadWriteProperty<in T, V>
類:表示委託的屬性是可讀寫的
// 委託寄存類
class DBDelegate<in T, V>(private val field: String,
private val id: Int) :
// T: 要代理的類型 , V: 屬性的類型
ReadWriteProperty<T, V> {
private val data = arrayOf<MutableMap<String, Any?>>(
mutableMapOf(
"id" to 1,
"age" to 12,
"name" to "Kyle",
"password" to "123456"
),
mutableMapOf(
"id" to 2,
"age" to 2000,
"name" to "Alien",
"password" to "654321"
)
)
override fun getValue(thisRef: T, property: KProperty<*>): V {
// firstOrNull 是語法糖
val cache = data.firstOrNull {
it["id"] == id
}?.get(field) ?: throw Exception("Cannot not find field: $field")
// println("Get field of $field value, $cache")
return cache as V
}
override fun setValue(thisRef: T, property: KProperty<*>, value: V) {
// println("Set field of $field value to $value")
data.firstOrNull {
it["id"] == id
}?.set(field, value)
}
}
class AccountUser(id: Int) {
// 要代理的類 AccountUser
// 返回值 String
var accountName : String by DBDelegate<AccountUser, String>("name", id)
var accountAge : Int by DBDelegate<AccountUser, Int>("age", id)
var accountPassword : String by DBDelegate<AccountUser, String>("password", id)
}
fun main() {
val account = AccountUser(1)
println(account.accountName)
println(account.accountAge)
println("origin: ${account.accountPassword}")
account.accountPassword = "Apple123"
println("changed: ${account.accountPassword}")
}
● 使用
DBDelegate
隔離數據的取得,管理數據,之後要修就不需要修改AccountUser
類
lateinit 懶加載
Kotlin 基於 Java NPE 提出空安全的概念,來避免 NPE,除非使用 ?
符號來標示該屬性可為 null
但如果要 自己負責屬性的狀態,則可以使用 lateinit
(關鍵字), by lazy
(lambda 函數)
延遲化 - lateinit
● Kotlin 延遲化就使用 關鍵字 lateinit 來描述該變量即可,這樣 1 一開始就不用賦值為 null,也就不用判空,2 但若是程式錯誤仍然會報錯,也就是說 lateinit 是省略的判空,而自身負責該變量的安全
● 基礎類型不允許使用 lateinit
先來看看一般使用延遲,就必須使用許多的判空符號,為此必須寫下許多不必要的程式
class MyClass(private var value: Int) {
fun setValue(v: Int) {
value = v
}
fun getValue() : Int? {
return value
}
fun addValue(v: Int) {
value +=v
}
fun reduceValue(v: Int) {
value -= v
}
}
class TestNormal {
private var m : MyClass? = null // 為此機制就必需使用許多的判空
fun setMyClass(myClass: MyClass) {
this.m = myClass
}
fun addTime(v: Int) {
repeat(v) {
m?.addValue(1) // 判空
m?.addValue(2) // 判空
m?.addValue(3) // 判空
m?.addValue(4) // 判空
m?.addValue(5) // 判空
}
}
fun reduceTime(v: Int) {
repeat(v) {
m?.reduceValue(1) // 判空
m?.reduceValue(2) // 判空
m?.reduceValue(3) // 判空
m?.reduceValue(4) // 判空
m?.reduceValue(5) // 判空
}
}
fun printValue() {
println("value: ${m?.getValue()}")
}
}
以下使用 lateinit 來描述 value 變量,就不必不斷判空,但 空指針必須自己負責
class TestLateInit {
private lateinit var m : MyClass // lateinit 可以省去 kotlin 判空檢查
fun setMyClass(myClass: MyClass) {
this.m = myClass
}
fun addTime(v: Int) {
repeat(v) {
m.addValue(1) // 判空
m.addValue(2) // 判空
m.addValue(3) // 判空
m.addValue(4) // 判空
m.addValue(5) // 判空
}
}
fun reduceTime(v: Int) {
repeat(v) {
m.reduceValue(1) // 判空
m.reduceValue(2) // 判空
m.reduceValue(3) // 判空
m.reduceValue(4) // 判空
m.reduceValue(5) // 判空
}
}
fun printValue() {
println("value: ${m.getValue()}")
}
}
fun main(){
val t = TestLateInit()
t.setMyClass(MyClass(0))
t.addTime(3)
t.printValue()
}
--實作結果--
--不檢查導致的空指針--
● 另外它可以使用一種特定的方式檢查,::<變量>.isInitialized 用來檢查是否已經初始化完成,當然它並不只可以檢查類的初始化,:: 還可以檢查其它判斷
// 使用 isInitialized
private lateinit var m : MyClass
fun addTime(v: Int) {
if(::m.isInitialized) {
repeat(v) {
m.addValue(1) // 判空
m.addValue(2) // 判空
m.addValue(3) // 判空
m.addValue(4) // 判空
m.addValue(5) // 判空
}
} else {
println("@addTime Haven't Init")
}
}
--實作結果--
by lazy:單次初始化
● 使用 by lazy{ }
就可以懶加載,並且 該懶加載初始化只會一次!並且只能使用 val
描述
這裡的
by
就是使用了屬性委託機制重寫 getter
val msg : String by lazy {
println("Msg init ~~") // 只會初始化一次
"Hello kotlin by lazy" // return value
}
fun main() {
println("first call: $msg")
println("\nsecond call: $msg")
}
lazy 源碼分析
A. 一般常用的 lazy 源碼是使用 SynchronizedLazyImpl
類
public actual fun <T> lazy(initializer: () -> T): Lazy<T>
= SynchronizedLazyImpl(initializer)
SynchronizedLazyImpl
類實作源碼:其重點是 1. 使用了 Volatile
儲存初始化完後的值,並 2. 使用 synchronized
同步初始化
internal object UNINITIALIZED_VALUE
private class SynchronizedLazyImpl<out T>(initializer: () -> T, lock: Any? = null) : Lazy<T>, Serializable {
private var initializer: (() -> T)? = initializer
@Volatile private var _value: Any? = UNINITIALIZED_VALUE
// final field is required to enable safe publication of constructed instance
private val lock = lock ?: this
override val value: T
get() {
val _v1 = _value
if (_v1 !== UNINITIALIZED_VALUE) {
@Suppress("UNCHECKED_CAST")
return _v1 as T
}
// 同步鎖
return synchronized(lock) {
val _v2 = _value
if (_v2 !== UNINITIALIZED_VALUE) {
@Suppress("UNCHECKED_CAST") (_v2 as T)
} else {
// 執行初始化函數
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)
}
Volatile 可以保證多執行序對於屬性操作的可見性
B. 可以指定 mode,每種 mode 都也不同的特性
mode | 說明 |
---|---|
NONE | 速度快,線程不安全 |
SYNCHRONIZED | 第一個執行的 Thread 進程初始化,之後不再初始化 |
PUBLICATION | 多個 Thread 可以安全訪問屬性(使用 CAS 機制) |
public actual fun <T> lazy(mode: LazyThreadSafetyMode, initializer: () -> T): Lazy<T> =
when (mode) {
LazyThreadSafetyMode.SYNCHRONIZED -> SynchronizedLazyImpl(initializer)
// 查看 SafePublicationLazyImpl
LazyThreadSafetyMode.PUBLICATION -> SafePublicationLazyImpl(initializer)
LazyThreadSafetyMode.NONE -> UnsafeLazyImpl(initializer)
}
SafePublicationLazyImpl
類實作源碼:其重點是1. 使用了 Volatile
儲存初始化完後的值,並 2. 使用 AtomicReferenceFieldUpdater
執行 CAS 機制
private class SafePublicationLazyImpl<out T>(initializer: () -> T) : Lazy<T>, Serializable {
@Volatile private var initializer: (() -> T)? = initializer
// 儲存更新的值
@Volatile private var _value: Any? = UNINITIALIZED_VALUE
// this final field is required to enable safe initialization of the constructed instance
private val final: Any = UNINITIALIZED_VALUE
override val value: T
get() {
val value = _value
if (value !== UNINITIALIZED_VALUE) {
@Suppress("UNCHECKED_CAST")
return value as T
}
val initializerValue = initializer
// if we see null in initializer here, it means that the value is already set by another thread
if (initializerValue != null) {
val newValue = initializerValue()
// 內部會不停循環比較
if (valueUpdater.compareAndSet(this, UNINITIALIZED_VALUE, newValue)) {
initializer = null
return newValue
}
}
@Suppress("UNCHECKED_CAST")
return _value as T
}
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)
companion object {
// 使用了 Java atomic 類
private val valueUpdater = java.util.concurrent.atomic.AtomicReferenceFieldUpdater.newUpdater(
SafePublicationLazyImpl::class.java,
Any::class.java,
"_value"
)
}
}
更多的 Kotlin 語言相關文章
在這裡,我們提供了一系列豐富且深入的 Kotlin 語言相關文章,涵蓋了從基礎到進階的各個方面。讓我們一起來探索這些精彩內容!
Kotlin 語言基礎
● Kotlin 語言基礎:想要建立堅實的 Kotlin 基礎?以下這些文章將帶你深入探索 Kotlin 的關鍵基礎和概念,幫你打造更堅固的 Kotlin 語言基礎
Kotlin 特性、特點
● Kotlin 特性、特點:探索 Kotlin 的獨特特性和功能,加深對 Kotlin 語言的理解,並增強對於語言特性的應用
Kotlin 進階:協程、響應式、異步
● Kotlin 進階:協程、響應式、異步:若想深入學習 Kotlin 的進階主題,包括協程應用、Channel 使用、以及 Flow 的探索,請查看以下文章