Kotlin基礎 — 委託

Elson_6發表於2018-03-03

目錄

  1. 類委託
  2. 屬性委託 2.1. 方法一:可以按需繼承 ReadOnlyProperty、ReadWriteProperty 兩個介面中的一個; 2.2. 方法二:自己定義,但方法引數必須和 1 中介面的方法引數一致;
  3. 標準委託 3.1. 延遲屬性 Lazy 3.2. 可觀察屬性 Observable 3.3. 把屬性儲存在對映中
  4. 使用場景

類委託(代理模式)

Delegation pattern

類的兩種代理方法

interface Base {
    fun print()
}

class BaseImpl(val x: Int) : Base {
    override fun print() { print(x) }
}
// 第一種:這裡直接使用關鍵字 by 進行代理
class Derived(base: Base) : Base by base
// 第二種:
class Derived(base: Base) : Base {
    val base : Base
    init {
        this.base = base
    }
    override fun print() {
        base.print() // base是BaseImpl的例項物件
    }
}

// 測試
class Main {
	fun main() {
	    val b = BaseImpl(10)
	    Derived(b).print() // prints 10
	}
}
複製程式碼

屬性委託

語法結構: val/var <property name>: <Type> by <expression>

兩種實現方法:

  1. 方法一:可以按需繼承 ReadOnlyProperty、ReadWriteProperty 兩個介面中的一個;

    // 讀取屬性
    public interface ReadOnlyProperty<in R, out T> {
        public operator fun getValue(thisRef: R, property: KProperty<*>): T
    }
    
    // 讀寫屬性
    public interface ReadWriteProperty<in R, T> {
    	// 關鍵字 operator 過載操作符
        public operator fun getValue(thisRef: R, property: KProperty<*>): T
        public operator fun setValue(thisRef: R, property: KProperty<*>, value: T)
    }
    複製程式碼

    示例程式碼:

    class Delegate<T> : ReadWriteProperty<Any?, T> {
    	// 重寫了getValue()方法
        override fun getValue(thisRef: Any?, property: KProperty<*>): T {
            return ...
        }
    
        override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
    
        }    
    }
    
    class Main {
    	var p: String by Delegate<String>()
    	fun test() {
    		val main = Main()
    		print(main.p)  // 會呼叫到Delegate中的getValue()方法
    		main.p = "Elson" // 會呼叫到Delegate中的setValue()方法
    	}
    }
    複製程式碼
  2. 方法二:自己定義,但方法引數必須和 1 中介面的方法引數一致;

    class Delegate<T> {
    	// 使用關鍵字 operator 來實現過載
        operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
            return "$thisRef, thank you for delegating '${property.name}' to me!"
        }
     
        operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
            println("$value has been assigned to '${property.name}' in $thisRef.")
        }
    }
    複製程式碼

    示例程式碼:

    class Main {
    	var p: String by Delegate<String>() //屬性 p 被代理
    	fun test() {
    		val main = Main()
    		print(main.p)  // 會呼叫到Delegate中的getValue()方法
    		main.p = "Elson" // 會呼叫到Delegate中的setValue()方法
    	}
    }
    複製程式碼

標準委託

1. 延遲屬性 Lazy

  1. 示例程式碼:

    val lazyValue: String by lazy {
        println("computed!")
        "Hello"
    }
    
    fun main(args: Array<String>) {
        println(lazyValue) //第一次呼叫lazyValue時,會先初始化lazy{}作用區間的程式碼,即列印一個"computed!",然後返回一個"Hello"字串並賦值給lazyValue;
        println(lazyValue) //第二次呼叫lazyValue時,直接將"Hello"賦值給lazyValue;可參考下面的原始碼
    }
    複製程式碼
  2. 原始碼:Lazy.kt 檔案

    public fun <T> lazy(initializer: () -> T): Lazy<T> = SynchronizedLazyImpl(initializer)
    
    private object UNINITIALIZED_VALUE
    
    @JvmVersion
    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!!()//第一次使用時執行這裡,將lazy大括號內的程式碼進行初始化,並返回最後一行的資料並賦值給typedValue
                        _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)
    }
    
    複製程式碼

2. 可觀察屬性 Observable

示例程式碼:

class User {
	// 方法一:
    var name: String by Delegates.observable("<no name>") {
        prop, old, new ->
        println("$old -> $new")
    }
	// 方法二:Delegates.observable()實際上就是對ObservableProperty的一層封裝
	var name : String by object : ObservableProperty<String>("<no name>") {
        override fun afterChange(property: KProperty<*>, oldValue: String, newValue: String) {
	        println("$oldValue -> $newValue")
        }
    }
}

fun main() {
    val user = User()
    user.name = "first"
    user.name = "second"
}
複製程式碼

輸出結果 < no name > -> first first -> second

原始碼:

public inline fun <T> observable(initialValue: T, crossinline onChange: (property: KProperty<*>, oldValue: T, newValue: T) -> Unit):
        ReadWriteProperty<Any?, T> = object : ObservableProperty<T>(initialValue) {
            override fun afterChange(property: KProperty<*>, oldValue: T, newValue: T) = onChange(property, oldValue, newValue)
}

public inline fun <T> vetoable(initialValue: T, crossinline onChange: (property: KProperty<*>, oldValue: T, newValue: T) -> Boolean):
        ReadWriteProperty<Any?, T> = object : ObservableProperty<T>(initialValue) {
	   // 需要返回一個boolean值,判斷是否需要發射,預設為true
            override fun beforeChange(property: KProperty<*>, oldValue: T, newValue: T): Boolean = onChange(property, oldValue, newValue)
}

public abstract class ObservableProperty<T>(initialValue: T) : ReadWriteProperty<Any?, T> {
    private var value = initialValue

    protected open fun beforeChange(property: KProperty<*>, oldValue: T, newValue: T): Boolean = true

    protected open fun afterChange (property: KProperty<*>, oldValue: T, newValue: T): Unit {}
    // 代理後,自動被呼叫
    public override fun getValue(thisRef: Any?, property: KProperty<*>): T {
        return value
    }
    // 代理後,自動被呼叫
    public override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        val oldValue = this.value
        // 判斷是否需要發射,預設為true
        if (!beforeChange(property, oldValue, value)) {
            return
        }
        this.value = value
        afterChange(property, oldValue, value)
    }
}
複製程式碼

3. 把屬性儲存在對映中

這部分不知道怎麼看原始碼

class User(val map: Map<String, Any?>) {
    val name: String by map
    val age: Int     by map
}

val user = User(mapOf(
    "name" to "John Doe",
    "age"  to 25
))
複製程式碼

相關文章