web-dev-qa-db-fra.com

Kotlin "La distribution intelligente est impossible car la propriété aurait pu être modifiée à ce moment-là"

Pourquoi Android Studio affiche-t-il l'erreur lorsque j'utilise le script n ° 2? Je n'ai pas trouvé de différence entre 1 et 2. 

class Adapter {
    var nameList : ArrayList<String>? = null
}

class Program {
    private fun send() {
        val list: ArrayList<String> = ArrayList()
        val adapter = Adapter()

// Case 1
        var otherList = adapter.nameList
        if (otherList != null) {
            list.addAll(otherList) // <--- no error
        }

// Case 2
        if (adapter.nameList!=null) {
            list.addAll(adapter.nameList) // <--- Error here
            // Smart cast to 'kotlin.collections.ArrayList<String> /* = Java.util.ArrayList<String> */' is impossible, because 'adapter.nameList' is a mutable property that could have been changed by this time
        }
    }
}

S'il vous plaît expliquer ce cas

11
quangkid

IDE devrait vous avertir en expliquant qu’après le contrôle de null, il est possible que adapter.nameList ait été modifié par un autre thread et que, lorsque vous appelez list.addAll(adapter.nameList), adapter.nameList puisse en réalité être null par ce point (encore thread aurait pu changer la valeur (ce serait une condition de concurrence critique).

Vous avez quelques solutions:

  1. Faites de nameList une val, ce qui rend sa référence final. Comme c'est final, il est garanti qu'un autre thread ne pourra pas le changer. Cela ne correspond probablement pas à votre cas d'utilisation.

    class Adapter {
        val nameList : ArrayList<String>? = null
    }
    
  2. Créez une copie locale de la liste de noms avant de procéder à la vérification. Comme il s'agit d'une copie locale, le compilateur sait qu'un autre thread ne peut pas y accéder et ne peut donc pas être modifié. La copie locale pourrait être définie avec soit une var ou une val dans ce cas, mais je recommande val.

    val nameList = adapter.nameList
    if (nameList != null) {
        list.addAll(nameList)
    }
    
  3. Utilisez l’une des fonctions utilitaires fournies par Kotlin pour ce type de cas. La fonction let copie la référence à laquelle elle est appelée en tant que paramètre à l'aide d'une fonction inline. Cela signifie que la compilation est identique à la n ° 2, mais un peu plus concise. Je préfère cette solution.

    adapter.nameList?.let { list.addAll(it) }
    
14
spierce7

Votre adaptateur.nameList est une propriété mutable, veuillez donc le convertir en immuable.

Utilisez ceci 

  val nameList : ArrayList<String>? = null

Au lieu de cela

  var nameList : ArrayList<String>? = null

Ou vous pouvez également résoudre ce problème en affirmant Assert non null

            list.addAll(adapter.nameList!!)

Remarque :- !! est évalué à l'exécution, c'est juste un opérateur.

L'expression (x !!)

lève une exception KotlinNullPointerException si x == null, .__ sinon elle renvoie x cast au type non nullable correspondant (par exemple, il le renvoie sous forme de chaîne lorsqu'il est appelé avec une variable de type String?).

4
vishal jangid

adapter.nameList est une propriété mutable qui aurait pu être modifiée`

La raison de cette vérification et du message d'erreur est threads . Ce que vous avez s'appelle une condition de course. Dans de nombreux cas similaires, il est possible pour un autre thread de modifier la valeur de adapter.namelist entre le contrôle de nullité et l'appel list.addAll. Clairement, cela ne peut pas arriver dans votre cas car l'adaptateur n'est pas filtré par la fonction d'envoi, mais je suppose que le compilateur n'est pas assez intelligent pour le savoir. 

En revanche, il n'y a pas de condition de concurrence critique dans le cas 1, car la liste de noms n'est accessible qu'une seule fois.

De plus, cela ne peut pas arriver si namelist est val plutôt que var - puisque le compilateur sait alors que cela ne peut pas changer - il ne peut donc pas changer de non-null à null.

1
Michael Anderson