如何在kotlin中为父映射中的child映射设置值?

How to set value to the child map in the father map in kotlin?

如标题,我有代码:

val description= mutableMapOf(
    "father2" to "123",
    "father" to mutableMapOf<String,String>())
description["father"]["child"] = "child value"

如果我试试这个:description["father"]["child"]="child value"

我会得到错误:

Unresolved reference. None of the following candidates is applicable because of receiver type mismatch: 
public inline operator fun <K, V> MutableMap<String!, String!>.set(key: String!, value: String!): Unit defined in kotlin.collections
public inline operator fun kotlin.text.StringBuilder /* = java.lang.StringBuilder */.set(index: Int, value: Char): Unit defined in kotlin.text

我该怎么办?

试试这个:

description["father"]?.put("child", "child value")

我目前的解决方案:

val description:MutableMap<String,Any> = mutableMapOf(
    "father" to "123"
)

val father2 = mutableMapOf<String,String>()
father2.put("child", "child value")
description["father2"]=father2

这是关于打字(或不打字)的问题。

description 地图由两对创建,一对从 StringString,另一对从 StringMutableMap<String, String>.这两个键都是 String,所以 Kotlin 会推断出 String 键的类型。但是价值观呢?它们是两个完全不相关的类型,除了 Any 之外没有共同的超类。所以description的类型被推断为MutableMap<String, Any>。 (您可以在 REPL 中查看。)

那么,当你提取一个值时,编译器能告诉它什么?几乎没有。这是一个 Any,因此您唯一可以确定的是它不为空。

这就是为什么 编译器没有像对待映射那样处理提取的值;据它所知,它可能 不是 地图 ! (我们 知道——但只是因为我们可以看到映射是用什么初始化的,并且从那时起它就没有被修改过,引用也没有与任何其他可以修改它的对象或线程共享. 但这是一种相对罕见的情况。)

Kotlin 是一种强类型语言,这意味着编译器会跟踪所有内容的类型,并且非常努力地不让您做任何可能导致 运行time 错误的事情。如果您的代码经过编译,则只要数据与您的预期不完全匹配,就会冒 ClassCastException 的风险,这很难成为好的程序。

那么,你能做什么?

最安全的做法是更改您的程序,这样您就不会拥有任何具有未知类型值的地图。显然,这取决于你的程序在做什么,所以我们不能在这里提出有用的建议。但是,如果您的 descriptionMutableMap<String, MutableMap<String, String>>,那么编译器会确切地知道所有内容是什么类型,并且您的预期代码会编译并且 运行 没问题。

另一个主要的替代方法是检查值是什么,然后再尝试将其视为地图。一种方法是使用 as? 安全转换运算符。检查一个值是否属于给定类型;如果是这样,则将其转换为该类型;否则,它 returns 为空。然后,您可以使用 .? 安全调用运算符仅在值不为空时调用方法。放在一起:

(description["father"] as? MutableMap<String, String>)?.put("child", "child value")

如果该值符合您的预期,则可以正常工作;如果没有,它将不执行任何操作并继续。

这有点冗长,但它会编译并且 运行 安全。或者,您可以像这样以更明确的方式做同样的事情:

val child = description["father"]
if (child is MutableMap<String, String>))
    child["child"] = "child value"

(在 if 中,编译器知道 child 的类型,并使用“智能转换”来允许 putter 调用。)

当然,这就更啰嗦了。但这就是您尝试针对类型系统工作所得到的结果:-)

(哦,顺便说一下,我认为递归数据结构的常用术语是“父”和“子”;我以前从未见过这样使用“父亲”。)

你可以试试这个:

val description = mutableMapOf<String?,MutableMap<String,String>?> 
("father" to mutableMapOf<String,String>())

description.get("father")?.put("Key","Value")
println(description) // {father={Key=Value}}