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
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:
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
}
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)
}
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) }
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?).
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.