そしてKotlinの初心者は、「なぜ次のコードがコンパイルされないのですか?」と尋ねます。
var left: Node? = null
fun show() {
if (left != null) {
queue.add(left) // ERROR HERE
}
}
'node'へのスマートキャストは不可能です。これは、 'left'はこの時点までに変更された可能性がある可変プロパティだからです。
left
は可変変数ですが、left != null
とleft
はNode
型であることを明示的にチェックしているので、その型にスマートキャストできないのはなぜですか。
どうすればこれをエレガントに修正できますか? :)
left != null
とqueue.add(left)
の実行の間に、別のスレッドがleft
の値をnull
に変更した可能性があります。
これを回避するにはいくつかの選択肢があります。ここにあるいくつかの:
Mfulton26の答えにあるものに加えて、4番目の選択肢があります。
?.
演算子を使用することで、let
を扱わずに、またはローカル変数を使用せずに、フィールドだけでなくメソッドも呼び出すことができます。
コンテキストのためのいくつかのコード:
var factory: ServerSocketFactory = SSLServerSocketFactory.getDefault();
socket = factory.createServerSocket(port)
socket.close()//smartcast impossible
socket?.close()//Smartcast possible. And works when called
それはメソッド、フィールド、そして私がそれを機能させるために私が試みた他のすべてのもので動作します。
そのため、手動キャストやローカル変数を使用する代わりに、この問題を解決するために、?.
を使用してメソッドを呼び出すことができます。
参考のために、これはKotlin 1.1.4-3
でテストされましたが、1.1.51
と1.1.60
でもテストされました。他のバージョンでも動作するという保証はありません。新しい機能かもしれません。
?.
演算子を使用することはあなたのケースでは使用できません。それは渡された変数なので問題です。 Elvis演算子は代替手段として使用することができます、そしてそれはおそらく最も少ないコードの量を必要とするものです。 continue
を使用する代わりに、return
を使用することもできます。
手動キャストを使用することも選択肢の1つですが、これは安全ではありません。
queue.add(left as Node);
左 が変更された場合 別のスレッドでは、プログラムがクラッシュします。
また、後でonCreate()
または他の場所で初期化を行う場合は、lateinit
を使用することもできます。
これを使って
lateinit var left: Node
これの代わりに
var left: Node? = null
これが機能しない実用的な理由は、スレッドには関係ありません。要は、node.left
は事実上node.getLeft()
に変換されるということです。
このプロパティゲッターは次のように定義されます。
val left get() = if (Math.random() < 0.5) null else leftPtr
したがって、2つの呼び出しが同じ結果を返さない可能性があります。
Null以外のアサーション演算子を使用してみてください...
queue.add(left!!)
これを行う:
var left: Node? = null
fun show() {
val left = left
if (left != null) {
queue.add(left) // safe cast succeeds
}
}
これは、受け入れられた回答によって提供される最初の選択肢のようですが、それがあなたが探しているものです。