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