Как установить значение для дочерней карты на родительской карте в котлине?

В качестве заголовка у меня есть код:

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}}
Другие вопросы по тегам