如何在 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地图由两对创建,一对从StringString,另一对从StringMutableMap<String, String>。 这两个键都是String的,所以 Kotlin 会推断出键类型的String。 但是价值观呢? 它们是两种完全不相关的类型,除了Any之外没有共同的超类。 因此,推断description的类型是MutableMap<String, Any>。 (您可以在例如 REPL 中检查。

那么,当你拉出一个值时,编译器能告诉它什么呢? 几乎没有。 这是一个Any,所以你唯一可以确定的是它不是空的。

这就是为什么编译器没有将拉出的值视为映射的原因;据它所知,它可能不是映射! (我们知道- 但只是因为我们可以看到映射是用什么初始化的,并且从那时起它没有被修改,引用也不会与任何其他可以修改它的对象或线程共享。 但这是一种相对罕见的情况。

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

那么,你能做什么呢?

最安全的做法是更改程序,以便没有任何具有未知类型值的映射。 显然,这取决于您的程序正在做什么,因此我们无法在此处提出有用的建议。 但是如果你的description是一个MutableMap<String, MutableMap<String, String>>,那么编译器就会确切地知道所有东西是什么类型,你预期的代码就会编译和运行良好。

另一个主要的替代方案是在尝试将其视为地图之前检查值是什么。 执行此操作的一种方法是使用as?安全转换运算符。 检查值是否属于给定类型;如果是这样,则将其强制转换为该类型;否则,它将返回 null。 然后,可以使用.?safe-call 运算符仅在值不为 null 时才调用方法。 把它放在一起:

(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的类型,并使用"智能强制转换"来允许推杆调用。

当然,这更啰嗦。 但这就是你从尝试对抗类型系统中获得的:-)

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

试试这个:

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

你可以试试这个:

val description = mutableMapOf<String?,MutableMap<String,String>?> 
("father" to mutableMapOf<String,String>())
description.get("father")?.put("Key","Value")
println(description) // {father={Key=Value}}

最新更新