web-dev-qa-db-fra.com

La conversion intelligente en "Type" est impossible car "variable" est une propriété mutable qui aurait pu être modifiée à ce moment-là.

Et le novice Kotlin demande: "Pourquoi le code suivant ne se compile-t-il pas?":

    var left: Node? = null

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

La conversion intelligente vers 'Node' est impossible, car 'left' est un mutable propriété qui aurait pu être changée à ce moment

J'obtiens que left est une variable mutable, mais je vérifie explicitement left != null et left est de type Node alors pourquoi ne peut-il pas être intelligemment configuré pour ce type?

Comment puis-je résoudre ce problème avec élégance? :)

142
FRR

Entre l'exécution de left != null et queue.add(left), un autre thread aurait pu remplacer la valeur de left par null.

Pour contourner ce problème, vous avez plusieurs options. Voilà quelque:

  1. Utilisez une variable locale avec smart cast:

    val node = left
    if (node != null) {
        queue.add(node)
    }
    
  2. Utilisez un appel sûr tel que l’un des suivants:

    left?.let { node -> queue.add(node) }
    left?.let { queue.add(it) }
    left?.let(queue::add)
    
  3. Utilisez l'opérateur Elvis avec return pour revenir plus tôt à partir de la fonction englobante:

    queue.add(left ?: return)
    

    Notez que break et continue peuvent être utilisés de la même manière pour les vérifications au sein de boucles.

197
mfulton26

Il existe une quatrième option en plus de celles de la réponse de mfulton26.

En utilisant l'opérateur ?., il est possible d'appeler des méthodes ainsi que des champs sans utiliser let ni utiliser des variables locales.

Un code pour le contexte:

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

Cela fonctionne avec les méthodes, les champs et toutes les autres choses que j'ai essayé de faire fonctionner.

Ainsi, pour résoudre le problème, vous pouvez utiliser ?. pour appeler les méthodes au lieu d'utiliser des conversions manuelles ou des variables locales.

Pour référence, cela a été testé dans Kotlin 1.1.4-3, mais également dans 1.1.51 et 1.1.60. Il n'y a aucune garantie que cela fonctionne sur d'autres versions, cela pourrait être une nouvelle fonctionnalité. 

L'utilisation de l'opérateur ?. ne peut pas être utilisée dans votre cas car c'est une variable passée qui pose problème. L'opérateur Elvis peut être utilisé comme alternative, et c'est probablement celui qui nécessite le moins de code. Au lieu d'utiliser continue cependant, return pourrait également être utilisé.

L'utilisation de la diffusion manuelle peut également être une option, mais ceci n'est pas null safe:

queue.add(left as Node);

Signification si laissé a changé sur un autre thread, le programme va planter.

16
Zoe

Vous pouvez également utiliser lateinit Si vous effectuez votre initialisation ultérieurement à onCreate() ou ailleurs.

Changement 

      var left: Node? = null

À

      lateinit var left: Node
6
Radesh

La raison pratique pour laquelle cela ne fonctionne pas n'est pas liée aux threads. Le fait est que node.left est effectivement traduit en node.getLeft().

Ce getter de propriété peut être défini comme:

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

Par conséquent, deux appels peuvent ne pas renvoyer le même résultat.

1
Roland Illig

Essayez d'utiliser l'opérateur d'assertion non nul ...

queue.add (à gauche !!) 

0
Bikeboy

Faire ceci:

var left: Node? = null

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

Ce qui semble être la première option fournie par la réponse acceptée, mais c'est ce que vous recherchez. 

0
EpicPandaForce