簡體   English   中英

如何實現適用於任意類型的屬性委托

[英]How to implement property delegate that works with an arbitrary type

我正在嘗試實現自定義屬性委托以將道具存儲在 Map 中(就像在docs中一樣,出於教育目的)

委托實施必須:

  1. 允許存儲任意類型的屬性
  2. 允許類型推斷(就像原來的那樣)

我寫了一個滿足第一個條件的代碼:

import kotlin.reflect.KProperty

fun main() {
    val e = Example(PropInMapDelegate())
    e.myProp = 1;
    println(e.myProp) // e.myProp is "Any?" :(
}

class Example(propInMapDelegate: PropInMapDelegate) {
    var myProp by propInMapDelegate
}

class PropInMapDelegate {
    private val _map: HashMap<String, Any?> = HashMap()

    operator fun getValue(thisRef: Any, property: KProperty<*>): Any? {
        return _map[property.name]
    }

    operator fun setValue(thisRef: Any, property: KProperty<*>, value: Any?) {
        _map[property.name] = value
    }

}

但是,它有兩個明顯的問題:

  1. 使用“任何?” 打破類型推斷
  2. var myProp: Int by propInMapDelegate會導致錯誤:

錯誤 #1

Property delegate must have a 'getValue(Example, KProperty*>)' method. None of the following functions are suitable.
getValue(Any?, KProperty<*>) defined in PropInMapDelegate

錯誤#2

Property delegate must have a 'setValue(Example, KProperty*>, Int)' method. None of the following functions are suitable.
setValue(Any?, KProperty<*>, Any?) defined in PropInMapDelegate

我嘗試使用 generics 來進行類型推斷,但失敗了。

像這樣更改PropInMapDelegate實現:

class PropInMapDelegate {
    private val _map: HashMap<String, Any?> = HashMap()

    operator fun <T>getValue(thisRef: Any, property: KProperty<*>): T? {
        return _map[property.name] as T // That feels very wrong
    }

    operator fun <T>setValue(thisRef: Any, property: KProperty<*>, value: T?) {
        _map[property.name] = value
    }

}

並使用以下

class Example(propInMapDelegate: PropInMapDelegate) {
    var myProp: Int by propInMapDelegate // error (see below)
}

導致:錯誤#1

Property delegate must have a 'getValue(Example, KProperty*>)' method. None of the following functions are suitable.
getValue(Any, KProperty<*>)   where T = Int for    operator fun <T> getValue(thisRef: Any, property: KProperty<*>): T? defined in PropInMapDelegate

錯誤#2

Property delegate must have a 'setValue(Example, KProperty*>, Int)' method. None of the following functions are suitable.
setValue(Any, KProperty<*>, Int?)   where T = Int for    operator fun <T> setValue(thisRef: Any, property: KProperty<*>, value: T?): Unit defined in PropInMapDelegate

問題:

  1. 我錯過了什么?
  2. 為什么 Kotlin 期望 setValue/getValue 簽名中的確切“Example”類型?
  3. 如何在不使用不安全的“as T”運算符的情況下實現“getValue”? 編輯:似乎原始實現也使用as它,所以它可能沒問題?
public inline operator fun <V, V1 : V> MutableMap<in String, out @Exact V>.getValue(thisRef: Any?, property: KProperty<*>): V1 =
    @Suppress("UNCHECKED_CAST") (getOrImplicitDefault(property.name) as V1)

我認為錯誤消息的措辭方式誤導了您。 “示例”不是不匹配的部分。 由於ExampleAny的子類型,因此您的getValue() function 簽名的那部分很好。

不匹配的部分是您要返回T? 但您正試圖將它用於不可為 null 的屬性。

如果您將屬性更改為可為空,它將起作用:

var myProp: Int? by propInMapDelegate

或者,如果您將運算符 function 更改為返回不可為 null 的值,它將起作用:

operator fun <T> getValue(thisRef: Any, property: KProperty<*>): T {
    return _map[property.name] as T
}

請注意,如果 map 中不存在該鍵的值並且您將其用於不可為 null 的屬性,則轉換as T將失敗,因為null無法轉換為不可為 null 的 T。

關於標記為 3 的問題:如您在標准庫源代碼中所見,如果您使用任意值類型的 map,則強制轉換是不可避免的。 這是為此目的使用 map 的潛在危險的一部分。 也有可能 map 根本沒有與請求的屬性名稱匹配的值。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM