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

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

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

Я понимаю, что left - изменяемая переменная, но я явно проверяю left! = Null и left имеет тип Node так почему его нельзя преобразовать в этот тип?

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

frankelot

Ответов: 12

Ответы (12)

Между выполнением left! = Null и queue.add (left) другой поток мог изменить значение left на null.

Чтобы обойти это, у вас есть несколько вариантов. Вот некоторые:

  1. Используйте локальную переменную с умным приведением:

     val узел = слева
     if (node! = null) {
         queue.add (узел)
     }
    
  2. Используйте безопасный вызов, например, один из следующих:

     осталось? .Let {node -> queue.add (node)}
     осталось? .let {queue.add (it)}
     осталось? .let (очередь :: добавить)
    
  3. Используйте оператор Элвиса с return до return раньше из включающей функции:

     queue.add (слева?: Возврат)
    

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

Сделайте так:

var left: Node? = null

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

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

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

Используйте это

lateinit var left: Node

Вместо этого

var left: Node? = null

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

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

В ответе 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);

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

Как бы я это написал:

var left: Node? = null

fun show() {
     val left = left ?: return
     queue.add(left) // no error because we return if it is null
}

Это сработало для меня: private lateinit var varName: String

Выполните следующие действия: -

var left: Node? = null

Использовать нулевой безопасный вызов

left?.let { node -> queue.add(node) } // The most preferred one

Изменить var осталось: Узел? = null до lateinit var left: Node. Проблема решена.

Ваше самое элегантное решение должно быть:

var left: Node? = null

fun show() {
    left?.also {
        queue.add( it )
    }
}

Тогда вам не нужно определять новую и ненужную локальную переменную, и у вас нет никаких новых утверждений или приведений (которые не являются DRY). Другие функции осциллографа также могут работать, поэтому выберите свой любимый.

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

Получатель этого свойства может быть определен как:

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

Следовательно, два вызова могут не возвращать одинаковый результат.

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

queue.add(left!!) 

Для возможности Smart Cast свойств, типом данных свойства должен быть класс, содержащий метод или поведение, к которому вы хотите получить доступ, а НЕ то, что свойство имеет тип суперкласса.


например, на Android

Be:

class MyVM : ViewModel() {
    fun onClick() {}
}

Решение:

From: private lateinit var viewModel: ViewModel
To: private lateinit var viewModel: MyVM

Использование:

viewModel = ViewModelProvider(this)[MyVM::class.java]
viewModel.onClick {}

GL

2022 WebDevInsider