Смарт-литье в "Тип" невозможно, потому что "переменная" является изменчивым свойством, которое к этому моменту могло быть изменено
И новичок из Kotlin спрашивает: "почему не скомпилируется следующий код?":
var left: Node? = null
fun show() {
if (left != null) {
queue.add(left) // ERROR HERE
}
}
Интеллектуальное приведение к "Узлу" невозможно, поскольку "left" является изменяемым свойством, которое могло быть изменено к этому времени.
Я получаю, что left
является изменяемой переменной, но я явно проверяю left != null
и left
имеет тип Node
так почему же он не может быть умно приведен к этому типу?
Как я могу это исправить элегантно? :)
Ответы
Ответ 1
Между выполнением left != null
queue.add(left)
и queue.add(left)
другой поток мог изменить значение left
на null
.
Чтобы обойти это, у вас есть несколько вариантов. Вот некоторые:
-
Используйте локальную переменную с умным приведением:
val node = left
if (node != null) {
queue.add(node)
}
-
Используйте безопасный вызов, например, один из следующих:
left?.let { node -> queue.add(node) }
left?.let { queue.add(it) }
left?.let(queue::add)
-
Используйте оператор Элвиса с 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
Поэтому два вызова могут не возвращать один и тот же результат.