Подтвердить что ты не робот

Смарт-литье в "Тип" невозможно, потому что "переменная" является изменчивым свойством, которое к этому моменту могло быть изменено

И новичок из Kotlin спрашивает: "почему не скомпилируется следующий код?":

    var left: Node? = null

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

Интеллектуальное приведение к "Узлу" невозможно, поскольку "left" является изменяемым свойством, которое могло быть изменено к этому времени.

Я получаю, что left является изменяемой переменной, но я явно проверяю left != null и left имеет тип Node так почему же он не может быть умно приведен к этому типу?

Как я могу это исправить элегантно? :)

4b9b3361

Ответ 1

Между выполнением left != null queue.add(left) и queue.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. Используйте оператор Элвиса с return чтобы досрочно вернуться из включающей функции:

    queue.add(left ?: return)
    

    Обратите внимание, что break и continue могут использоваться аналогично для проверок внутри циклов.

Ответ 2

В дополнение к ответам mfulton26 есть четвертый вариант.

Используя оператор ?., вы можете вызывать методы, а также поля, не имеющие отношения к 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. Там нет гарантии, что он работает с другими версиями, это может быть новая функция.

Использование оператора ?. не может использоваться в вашем случае, так как это переданная переменная, что проблема. В качестве альтернативы можно использовать оператор Элвиса, и, вероятно, тот, который требует наименьшего количества кода. Вместо использования continue, однако, можно использовать return.

Использование ручного литья также может быть вариантом, но это небезопасно:

queue.add(left as Node);

Значение, если влево изменилось на другой поток, программа выйдет из строя.

Ответ 3

Также вы можете использовать lateinit если вы выполните инициализацию позже в onCreate() или в другом месте.

Использовать этот

lateinit var left: Node

Вместо этого

var left: Node? = null

И есть другой способ, которым пользуются !! конец переменной, когда вы используете это так

queue.add(left!!) // add !!

Ответ 4

Сделай это:

var left: Node? = null

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

Который, кажется, является первым вариантом, предоставленным принятым ответом, но это то, что вы ищете.

Ответ 5

Попробуйте использовать оператор утверждения not-null...

queue.add(left!!) 

Ответ 6

Практическая причина, почему это не работает, не связана с потоками. Дело в том, что node.left эффективно переводится в node.getLeft().

Это свойство get может быть определено как:

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

Поэтому два вызова могут не возвращать один и тот же результат.