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


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

    var left: Node? = null

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

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

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

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

2 109

2 ответа:

между исполнением left != null и 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 для раннего возврата из функции enclosing:

    queue.add(left ?: return)
    

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

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

С помощью ?. оператор можно вызывать методы, а также поля, не имея дело с let или с помощью локальных переменных.

код для контекста:

var factory: ServerSocketFactory = SSLServerSocketFactory.getDefault();
socket = factory.createServerSocket(port)
socket.close()//smartcast impossible
socket?.close()//Smartcast possible. And works when called

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

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

для справки, это было протестировано в Котлине 1.1.4-3, но также проверено в 1.1.51 и 1.1.60. Нет никакой гарантии, что это работает на других версиях, это может быть новая функция.

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

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

queue.add(left as Node);

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