これはタイピング (またはタイピングの欠如) に関するものです。
description
マップは 2 つのペアから作成されます。1 つは a から aへString
、もう 1 つは aString
から aString
へMutableMap<String, String>
です。キーは bothString
であるため、Kotlin はString
キーのタイプを推測します。しかし、値はどうですか?これらは 2 つの完全に無関係な型であり、 以外に共通のスーパークラスはありませんAny
。したがって、 の型は でdescription
あると推測されますMutableMap<String, Any>
。(たとえば、REPL で確認できます。)
では、値を引き出すと、コンパイラはそれについて何を知ることができるでしょうか? ほとんど何もありません。であるAny
ため、null でないことだけは確認できます。
そのため、コンパイラはその引き出された値をマップのように扱っていません。それが知る限り、それは地図ではないかもしれません! (私たちは知っています — しかし、マップが初期化されたものと、それ以降変更されておらず、それを変更できる他のオブジェクトやスレッドと参照が共有されていないことを確認できるためです。しかし、それは比較的まれな状況です.)
Kotlin は厳密に型指定された言語です。つまり、コンパイラはすべての型を追跡し、実行時エラーを引き起こす可能性のある操作をユーザーに行わせないように努めます。コードがコンパイルされた場合ClassCastException
、データが期待したものと正確に一致しない場合は常にリスクが発生し、これは優れたプログラムへの道ではありません。
それで、あなたは何ができますか?
最も安全な方法は、不明な型の値を持つマップがないようにプログラムを変更することです。明らかに、それはあなたのプログラムが何をしているかに依存するので、ここで有益な提案をすることはできません. しかし、あなたdescription
が の場合MutableMap<String, MutableMap<String, String>>
、コンパイラはすべてがどのタイプであるかを正確に認識し、意図したコードはコンパイルして正常に実行されます。
他の主な代替手段は、値を map のように扱う前に確認することです。これを行う 1 つの方法は、as?
safe-cast 演算子を使用します。値が特定の型であるかどうかをチェックします。その場合、その型にキャストされます。それ以外の場合は null を返します。その後、.?
安全呼び出し演算子を使用して、値が 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
「スマート キャスト」を使用してパター呼び出しを許可します。)
もちろん、それはさらに長ったらしいです。しかし、それは型システムに反対しようとすることから得られるものです:-)
(ちなみに、再帰的なデータ構造の通常の用語は「親」と「子」だと思います。「父」がそのように使用されているのを見たことがありません。)