【问题标题】:Kotlin MutableMap getter and setter not working as expectedKotlin MutableMap getter 和 setter 没有按预期工作
【发布时间】:2020-04-09 20:07:10
【问题描述】:

在一个类中,我有一个 MutableMap,我想声明一个 getter 和一个 setter,如下所示:

open class StringList() {
    private val list= mutableListOf<String>()
    var values: MutableMap<String, String>
        get() {
            println("get member")        // this is printed.. twice
            return mutableMapOf<String, String>()
        }
        set(value) {
            println("set member")        // this is not printed
        }
     fun add(s: String, aObject: Any? = null): Int {
        list.add(s)
        return list.count() - 1
    }
}

但是当我运行代码时,像这样:

var sl = StringList()
sl.add("user=amo")
sl.values["user"] = "other"
val r = sl.values["user"]

我意识到 getter 执行了两次,而 setter 则没有执行。

我做错了什么?

【问题讨论】:

  • 也许我理解错了,但你从来没有调用过setter,你调用了两次getter。调用 setter 将是 s1.values = mutableMapOf 等,但您将 s1.values 引用两次作为 getter,第一次然后设置一个条目位而不设置地图

标签: kotlin operator-overloading


【解决方案1】:

好的,我想我明白你想要做什么。您希望在从 MutableMap 添加或检索值时发生一些副作用,对吗?

属性的 getter 和 setter 与此无关。它们是属性的 getter 和 setter,是对 MutableMap 的引用。它们不是地图本身值的获取者和设置者。

您可以使用两种不同的策略来执行此操作:

1) 创建您自己的具有副作用的地图类。最简单的方法是继承现有的 MutableMap 实现,如 HashMap。

open class StringList() {
    private val list = mutableListOf<String>()
    val values = object: HashMap<String, String>(){
        override fun get(key: String): String? {
            println("Retrieved map value for key $key") // Side effect here
            return super.get(key)
        }

        override fun put(key: String, value: String): String? {
            println("Put key value pair $key / $value") // Side effect here
            return super.put(key, value)
        }
    }

    //...
}

2) 编写您自己的访问器,将调用传递给地图访问器并将您的副作用放在那里。这将迫使您调用这些特定函数,而不是直接从类外部访问地图,因此您可能希望将地图属性设为protectedprivate

open class StringList() {
    private val list = mutableListOf<String>()
    private val values = mutableMapOf<String, String>()

    //...

    fun putMapValue(key: String, value: String) {
        println("Put key value pair $key / $value") // Side effect here
        values[key] = value
    }

    fun getMapValue(key: String): String? {
        println("Retrieved map value for key $key") // Side effect here
        return values[key]
    }
}

【讨论】:

    【解决方案2】:

    发生这种情况是因为变量的setter 在其content 更改时不会被调用,但只有在有分配时才会被调用,在您的情况下,您从未分配过它。

    var sl = StringList()
    // Doesn't invoke the setter.
    sl.add("user=amo")
    // .values invokes the getter.
    sl.values["user"] = "other"
    // .values invokes the getter again.
    val r = sl.values["user"]
    // Invokes the setter since you made an assignment (this line is just an example on how to invoke the setter).
    sl.values = mutableMapOf()
    

    【讨论】:

    • 那么不可能有sl.values["user"] = "other"这种语法来触发setter??
    • 不,我认为您对 getter 和 setter 的作用存在根本性的误解。设置器更改映射sl 的值。如果不向它传递一个新值,就无法触发 setter。在这种情况下,新值将是一个全新的 MutableMap。您的 getter 也存在根本性的缺陷,因为它每次访问时都会返回一个全新的 MutableMap,因此一旦您对其进行修改,并稍后返回从中检索值,这些值就会消失。
    • 此语法转换为sl.getValues().set("user", "other")。不,你不能让它调用setValues()
    • 很好的解释@Tenfour04,让我回顾一下我的代码:)
    • 我现在看到了你的问题,Tenfour04 和 AlexeyRomanov 都说了。
    【解决方案3】:

    几天前我用List 代替MutableMap 回答了类似的问题,请参阅here

    这里是MutableMap的版本

    class MMap<T, U>(
            private val map: MutableMap<T, U> = mutableMapOf(),
            private val getter: ((T) -> Unit)? = null,
            private val setter: ((U) -> Unit)? = null
    ) : MutableMap<T, U> by map {
    
        override fun put(key: T, value: U): U? = 
            map.put(key, value).also { setter?.invoke(value) }
    
        override fun get(key: T): U? = 
             map[key].also { getter?.invoke(key) }
    }
    

    用法:

    var values = MMap<String, String>(
        getter = { println("get member $it") },
        setter = { println("set member $it") }
    )
    

    【讨论】:

    • 感谢您的回答。好的,这是 getter 的正确行为,但是你知道我怎样才能拥有像 l.values["user"] = "other" 这样的语法并通过 setter 吗??
    • 以我的其他答案为起点。我会尽快更新我的代码
    • 更新了我的预览答案的地图版本
    猜你喜欢
    • 2019-09-03
    • 1970-01-01
    • 2021-08-14
    • 1970-01-01
    • 1970-01-01
    • 2016-10-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多