Nullable Var und Smart Cast
Betrachten Sie den folgenden Block von Kotlin.
var nullableInt: Int? = null
if (nullableInt != null) {
val checkedInt: Int = nullableInt
print("not-null-branch")
} else {
print("null-branch")
}
Android Studio sagt mir, dass die intelligente Besetzung von Int?bis Intnicht möglich ist, da sie nullableIntveränderlich ist. Ich verstehe, dass dies ein Problem im Multithread-Code sein kann.
Eine Möglichkeit, das Problem zu lösen, besteht darin, eine explizite Besetzung mit vorzunehmen. val checkedInt: Int = nullableInt!!Wenn ich die Codierung jedoch in einer Multithread-Umgebung verwenden würde, ist dies nicht ratsam.
Duplikate schließen
Zu diesem Thema gibt es einige sehr enge Fragen zu SO. Ich finde jedoch keine zufriedenstellende Antwort in einer der Antworten, die ich gefunden habe:
In Kotlin wird auf idiomatische Weise mit nullbaren Werten umgegangen, referenziert oder konvertiert, warum das Problem auftritt, es wird jedoch kein Vorschlag zur Behandlung des Problems gemacht
Kotlin Smart Cast für Nullprüfung vermeiden hat einen Wenn-nicht-Null-Zweig, der einen Nicht-Null-Wert zurückgibt, sodass das ?.let{} :? {}Konstrukt dort funktioniert. Da mein Nicht-Null-Zweig Null zurückgibt, würden beide Zweige ausgeführt.
Kotlin "Smart Cast ist unmöglich, da die Eigenschaft zu diesem Zeitpunkt geändert werden könnte" betrifft nur einen Nicht-Null-Zweig und keinen Null-Zweig, daher ?.let{}scheint das Konstrukt korrekt zu sein. In diesem Thread geben sie den Vorschlag, vor der ifAnweisung eine lokale Kopie zu erstellen, was auch in meinem Fall möglich sein könnte. Es ist leider nicht sehr elegant und ich hoffe, es gibt eine andere Alternative.
Gibt es eine Möglichkeit, diese nullbedingte Verzweigung nullsicher zu handhaben, ohne eine Kopie zu erstellen?
Ich verstehe, dass die Antwort möglicherweise "es kommt darauf an" sein könnte. Wenn dies der Fall ist, sagen Sie dies bitte und erläutern Sie, warum.
Antworten
Verwenden Sie .letanstelle von?.let
Da die .letErweiterungsfunktion für alle Typen definiert ist, einschließlich nullter, können Sie sie tatsächlich ohne den Safe-Call- ?.Operator aufrufen . Wenn Sie das tun, wird das Lambda immer aufgerufen, auch für nullWerte. Der Parameter innerhalb des letBlocks ist nullwertfähig, wenn der Empfänger nullwertfähig ist.
Der Lambda-Parameter kann jedoch für Smart Casting verwendet werden , da er nicht veränderbar ist.
Hier ist der Unterschied:
x.let { it -> /* This always runs. 'it' can be null if 'x' is null */ }
x?.let { it -> /* This only runs if 'x' is not null. 'it' is never null. */ }
Wenn Sie das auf Ihr Beispiel anwenden, könnten Sie Folgendes schreiben:
var nullableInt: Int? = null
nullableInt.let {
if (it != null) {
doSomethingWith(it)
} else {
doSomethingElse()
}
}
Sie können eine Eigenschaft nicht starten, da sie möglicherweise von einem anderen Thread geändert wird. Daran führt kein logischer Weg vorbei. Die Sprache bietet bereits eine unsichere Möglichkeit, dies mit der !!bereits erwähnten Sprache zu tun .
Das Erstellen einer lokalen Kopie der Referenz (entweder manuell oder mithilfe einer Bereichsfunktion wie withoder let) ist trivial, sodass Sie sich keine Sorgen machen müssen.
Meine persönliche Meinung ist, dass eine lokale Variable der sauberste und am besten lesbare Weg ist, dies zu tun. Sie vermeiden das Verschachteln von Blöcken mit Bereichsfunktionen.
val myVar = myProp
if (myVar != null) {
} else {
}
Ich denke, der sauberste Weg mit Scope-Funktionen ist die Verwendung with. Es liest sich besser als letbei beiden Zweigen.
with(myProp) {
if (this != null) {
} else {
}
}
Im Folgenden wird beschrieben, wie Sie beide Zweige präzise handhaben können. Ich halte alsoes für etwas robuster als letin dieser Situation, da Sie nicht versehentlich beide Zweige ausführen können, indem Sie eine Null vom ersten Lambda zurückgeben. Die Lesbarkeit leidet jedoch darunter.
myProp?.also {
// not null it
} ?: run {
// null
}