web-dev-qa-db-ja.com

'variable'はこの時点までに変更された可能性がある可変プロパティであるため、 'Type'へのスマートキャストは不可能です。

そしてKotlinの初心者は、「なぜ次のコードがコンパイルされないのですか?」と尋ねます。

    var left: Node? = null

    fun show() {
         if (left != null) {
             queue.add(left) // ERROR HERE
         }
    }

'node'へのスマートキャストは不可能です。これは、 'left'はこの時点までに変更された可能性がある可変プロパティだからです。

leftは可変変数ですが、left != nullleftNode型であることを明示的にチェックしているので、その型にスマートキャストできないのはなぜですか。

どうすればこれをエレガントに修正できますか? :)

166
FRR

left != nullqueue.add(left)の実行の間に、別のスレッドがleftの値をnullに変更した可能性があります。

これを回避するにはいくつかの選択肢があります。ここにあるいくつかの:

  1. スマートキャストでローカル変数を使用します。

    val node = left
    if (node != null) {
        queue.add(node)
    }
    
  2. 次のいずれかのように 安全な呼び出し を使用します。

    left?.let { node -> queue.add(node) }
    left?.let { queue.add(it) }
    left?.let(queue::add)
    
  3. 囲んでいる関数から早く戻るには、 Elvis演算子return と共に使用します。

    queue.add(left ?: return)
    

    breakcontinueは、ループ内のチェックにも同様に使用できます。

233
mfulton26

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.511.1.60でもテストされました。他のバージョンでも動作するという保証はありません。新しい機能かもしれません。

?.演算子を使用することはあなたのケースでは使用できません。それは渡された変数なので問題です。 Elvis演算子は代替手段として使用することができます、そしてそれはおそらく最も少ないコードの量を必要とするものです。 continueを使用する代わりに、returnを使用することもできます。

手動キャストを使用することも選択肢の1つですが、これは安全ではありません。

queue.add(left as Node);

が変更された場合 別のスレッドでは、プログラムがクラッシュします。

16
Zoe

また、後でonCreate()または他の場所で初期化を行う場合は、lateinitを使用することもできます。

これを使って

lateinit var left: Node

これの代わりに

var left: Node? = null
10
Radesh

これが機能しない実用的な理由は、スレッドには関係ありません。要は、node.leftは事実上node.getLeft()に変換されるということです。

このプロパティゲッターは次のように定義されます。

val left get() = if (Math.random() < 0.5) null else leftPtr

したがって、2つの呼び出しが同じ結果を返さない可能性があります。

1
Roland Illig

Null以外のアサーション演算子を使用してみてください...

queue.add(left!!) 
0
Bikeboy

これを行う:

var left: Node? = null

fun show() {
     val left = left
     if (left != null) {
         queue.add(left) // safe cast succeeds
     }
}

これは、受け入れられた回答によって提供される最初の選択肢のようですが、それがあなたが探しているものです。

0
EpicPandaForce