Как установить значение для дочерней карты на родительской карте в котлине?
В качестве заголовка у меня есть код:
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
Как я могу сделать?
4 ответа
Речь идет о наборе (или его отсутствии).
В description
карта создается из двух пар, одна из String
к String
, а другой из String
к MutableMap<String, String>
. Ключи обаString
, поэтому Котлин сделает вывод String
по типу ключа. А как насчет ценностей? Это два совершенно не связанных типа, у которых нет общего суперкласса, кромеAny
. Итак, типdescription
предполагается быть MutableMap<String, Any>
. (Вы можете проверить это, например, в REPL.)
Итак, когда вы извлекаете значение, что компилятор может об этом сказать? Почти ничего. ЭтоAny
, поэтому единственное, в чем вы можете быть уверены, - это то, что он не равен нулю.
Вот почему компилятор не обрабатывает это извлеченное значение как карту; насколько ему известно, это может быть не карта! (Мы знаем, но только потому, что можем видеть, чем была инициализирована карта, и что с тех пор она не изменялась, а также ссылка на какие-либо другие объекты или потоки, которые могли бы ее изменить. Но это относительно редкое обстоятельство.)
Kotlin - это строго типизированный язык, что означает, что компилятор отслеживает тип всего и очень старается не позволять вам делать что-либо, что может вызвать ошибку времени выполнения. Ваш код, если он скомпилирован, рискуетClassCastException
каждый раз, когда данные не совсем соответствовали вашим ожиданиям, что вряд ли приведет к хорошим программам.
Так что ты можешь сделать?
Самым безопасным было бы изменить вашу программу, чтобы у вас не было карт с неизвестными типами значений. Очевидно, это зависит от того, что делает ваша программа, поэтому мы не можем делать здесь полезные предложения. Но если твойdescription
были MutableMap<String, MutableMap<String, String>>
, то компилятор будет точно знать, что это за тип, и ваш предполагаемый код будет компилироваться и работать нормально.
Другой основной альтернативой было бы проверить, какое значение имеет значение, прежде чем пытаться рассматривать его как карту. Один из способов сделать это - использоватьas?
оператор безопасного приведения. Это проверяет, принадлежит ли значение заданному типу; если да, то он приводится к этому типу; в противном случае возвращается значение 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}}