Zunächst sollten Sie alles über Null Safety in Kotlin lesen, das die Fälle gründlich abdeckt.
In Kotlin können Sie nicht auf einen nullbaren Wert zugreifen, ohne sicher zu sein, dass dies nicht der Fall ist null
(unter Bedingungen auf null prüfen ) oder zu behaupten, dass null
der !!
sichere Operator sicher nicht verwendet wird , mit einem ?.
sicheren Aufruf darauf zugegriffen wird oder zuletzt etwas angegeben wird, das möglicherweise null
ein Wert ist Standardwert mit dem ?:
Elvis-Operator .
Für Ihren ersten Fall in Ihrer Frage haben Sie Optionen, abhängig von der Absicht des Codes, den Sie verwenden würden. Eine davon ist idiomatisch, hat jedoch unterschiedliche Ergebnisse:
val something: Xyz? = createPossiblyNullXyz()
// access it as non-null asserting that with a sure call
val result1 = something!!.foo()
// access it only if it is not null using safe operator,
// returning null otherwise
val result2 = something?.foo()
// access it only if it is not null using safe operator,
// otherwise a default value using the elvis operator
val result3 = something?.foo() ?: differentValue
// null check it with `if` expression and then use the value,
// similar to result3 but for more complex cases harder to do in one expression
val result4 = if (something != null) {
something.foo()
} else {
...
differentValue
}
// null check it with `if` statement doing a different action
if (something != null) {
something.foo()
} else {
someOtherAction()
}
Lesen Sie für "Warum funktioniert es, wenn null aktiviert ist" die folgenden Hintergrundinformationen zu Smart Casts .
Verwenden Map
Sie für Ihren zweiten Fall in Ihrer Frage in der Frage mit , wenn Sie als Entwickler sicher sind, dass das Ergebnis niemals eintreten wird null
, einen !!
sicheren Operator als Aussage:
val map = mapOf("a" to 65,"b" to 66,"c" to 67)
val something = map.get("a")!!
something.toLong() // now valid
oder in einem anderen Fall, wenn die Karte eine Null zurückgeben KÖNNTE, Sie aber einen Standardwert angeben können, hat sie Map
selbst eine getOrElse
Methode :
val map = mapOf("a" to 65,"b" to 66,"c" to 67)
val something = map.getOrElse("z") { 0 } // provide default value in lambda
something.toLong() // now valid
Hintergrundinformation:
Hinweis: In den folgenden Beispielen verwende ich explizite Typen, um das Verhalten zu verdeutlichen. Bei der Typinferenz können die Typen normalerweise für lokale Variablen und private Mitglieder weggelassen werden.
Mehr über den !!
sicheren Operator
Der !!
Operator behauptet, dass der Wert kein null
NPE ist oder wirft. Dies sollte in Fällen verwendet werden, in denen der Entwickler garantiert, dass der Wert niemals erreicht wird null
. Betrachten Sie es als eine Behauptung, gefolgt von einer klugen Besetzung .
val possibleXyz: Xyz? = ...
// assert it is not null, but if it is throw an exception:
val surelyXyz: Xyz = possibleXyz!!
// same thing but access members after the assertion is made:
possibleXyz!!.foo()
Lesen Sie mehr: !! Sicherer Betreiber
Weitere null
Informationen zu Checking und Smart Casts
Wenn Sie Zugriff auf einen Nullable Type mit einem Schutz zu null
überprüfen, wird der Compiler SMART wirft den Wert innerhalb des Körpers der Erklärung als nicht-nullable. Es gibt einige komplizierte Abläufe, in denen dies nicht möglich ist, aber in häufigen Fällen funktioniert dies einwandfrei.
val possibleXyz: Xyz? = ...
if (possibleXyz != null) {
// allowed to reference members:
possiblyXyz.foo()
// or also assign as non-nullable type:
val surelyXyz: Xyz = possibleXyz
}
Oder wenn Sie is
nach einem nicht nullbaren Typ suchen:
if (possibleXyz is Xyz) {
// allowed to reference members:
possiblyXyz.foo()
}
Und das Gleiche gilt für 'Wann'-Ausdrücke, die auch sicher besetzt sind:
when (possibleXyz) {
null -> doSomething()
else -> possibleXyz.foo()
}
// or
when (possibleXyz) {
is Xyz -> possibleXyz.foo()
is Alpha -> possibleXyz.dominate()
is Fish -> possibleXyz.swim()
}
Einige Dinge erlauben es nicht, die null
Prüfung für die spätere Verwendung der Variablen intelligent zu machen . Das obige Beispiel verwendet eine lokale Variable, die in keiner Weise in den Fluss der Anwendung mutiert sein können, ob val
oder var
diese Variable keine Gelegenheit hatte , in eine mutieren null
. In anderen Fällen, in denen der Compiler die Flussanalyse nicht garantieren kann, ist dies ein Fehler:
var nullableInt: Int? = ...
public fun foo() {
if (nullableInt != null) {
// Error: "Smart cast to 'kotlin.Int' is impossible, because 'nullableInt' is a mutable property that could have been changed by this time"
val nonNullableInt: Int = nullableInt
}
}
Der Lebenszyklus der Variablen nullableInt
ist nicht vollständig sichtbar und kann von anderen Threads zugewiesen werden. Die null
Prüfung kann nicht intelligent in einen nicht nullbaren Wert umgewandelt werden. Eine Problemumgehung finden Sie im Thema "Sichere Anrufe".
Ein weiterer Fall, dem ein Smart Cast nicht vertrauen kann, um nicht zu mutieren, ist eine val
Eigenschaft für ein Objekt, das über einen benutzerdefinierten Getter verfügt. In diesem Fall hat der Compiler keine Sichtbarkeit darüber, was den Wert mutiert, und daher wird eine Fehlermeldung angezeigt:
class MyThing {
val possibleXyz: Xyz?
get() { ... }
}
// now when referencing this class...
val thing = MyThing()
if (thing.possibleXyz != null) {
// error: "Kotlin: Smart cast to 'kotlin.Int' is impossible, because 'p.x' is a property that has open or custom getter"
thing.possiblyXyz.foo()
}
Lesen Sie mehr: Überprüfen auf Null in Bedingungen
Weitere Informationen zum ?.
Safe Call-Betreiber
Der Operator für sichere Aufrufe gibt null zurück, wenn der Wert links null ist, andernfalls wird der Ausdruck rechts weiter ausgewertet.
val possibleXyz: Xyz? = makeMeSomethingButMaybeNullable()
// "answer" will be null if any step of the chain is null
val answer = possibleXyz?.foo()?.goo()?.boo()
Ein weiteres Beispiel, bei dem Sie eine Liste iterieren möchten, aber nur, wenn sie null
nicht leer ist, ist der Operator für sichere Anrufe wieder nützlich:
val things: List? = makeMeAListOrDont()
things?.forEach {
// this loops only if not null (due to safe call) nor empty (0 items loop 0 times):
}
In einem der obigen Beispiele hatten wir einen Fall, in dem wir eine if
Überprüfung durchgeführt haben, aber die Möglichkeit hatten, dass ein anderer Thread den Wert mutierte und daher keine intelligente Besetzung . Wir können dieses Beispiel ändern, um den Operator für sichere Anrufe zusammen mit der let
Funktion zur Lösung dieses Problems zu verwenden:
var possibleXyz: Xyz? = 1
public fun foo() {
possibleXyz?.let { value ->
// only called if not null, and the value is captured by the lambda
val surelyXyz: Xyz = value
}
}
Lesen Sie mehr: Sichere Anrufe
Mehr über die ?:
Elvis Operator
Mit dem Elvis-Operator können Sie einen alternativen Wert angeben, wenn ein Ausdruck links vom Operator lautet null
:
val surelyXyz: Xyz = makeXyzOrNull() ?: DefaultXyz()
Es hat auch einige kreative Verwendungszwecke, zum Beispiel eine Ausnahme auslösen, wenn etwas ist null
:
val currentUser = session.user ?: throw Http401Error("Unauthorized")
oder früh von einer Funktion zurückzukehren:
fun foo(key: String): Int {
val startingCode: String = codes.findKey(key) ?: return 0
// ...
return endingValue
}
Lesen Sie mehr: Elvis Operator
Nulloperatoren mit verwandten Funktionen
Kotlin stdlib hat eine Reihe von Funktionen, die mit den oben genannten Operatoren sehr gut funktionieren. Beispielsweise:
// use ?.let() to change a not null value, and ?: to provide a default
val something = possibleNull?.let { it.transform() } ?: defaultSomething
// use ?.apply() to operate further on a value that is not null
possibleNull?.apply {
func1()
func2()
}
// use .takeIf or .takeUnless to turn a value null if it meets a predicate
val something = name.takeIf { it.isNotBlank() } ?: defaultName
val something = name.takeUnless { it.isBlank() } ?: defaultName
Verwandte Themen
In Kotlin versuchen die meisten Anwendungen, null
Werte zu vermeiden , dies ist jedoch nicht immer möglich. Und manchmal null
macht es vollkommen Sinn. Einige Richtlinien zum Nachdenken:
In einigen Fällen werden verschiedene Rückgabetypen garantiert, die den Status des Methodenaufrufs und das Ergebnis bei Erfolg enthalten. Bibliotheken wie Result geben Ihnen einen Erfolgs- oder Fehlerergebnistyp, der auch Ihren Code verzweigen kann. Und die Promises-Bibliothek für Kotlin namens Kovenant macht dasselbe in Form von Versprechungen.
Für Sammlungen als Rückgabetypen wird immer eine leere Sammlung anstelle von a zurückgegeben null
, es sei denn, Sie benötigen den dritten Status "nicht vorhanden". Kotlin verfügt über Hilfsfunktionen wie emptyList()
oderemptySet()
zum Erstellen dieser leeren Werte.
Wenn Sie Methoden verwenden, die einen nullbaren Wert zurückgeben, für den Sie einen Standardwert oder eine Alternative haben, verwenden Sie den Elvis-Operator, um einen Standardwert anzugeben. Im Fall einer Map
Verwendung getOrElse()
, bei der anstelle einer Map
Methode, get()
die einen nullbaren Wert zurückgibt, ein Standardwert generiert werden kann. Gleiches gilt fürgetOrPut()
Wenn Sie Methoden aus Java überschreiben, bei denen Kotlin nicht sicher ist, ob der Java-Code nullbar ist, können Sie die ?
Nullbarkeit jederzeit aus Ihrer Überschreibung entfernen, wenn Sie sicher sind, wie die Signatur und Funktionalität lauten soll. Daher ist Ihre überschriebene Methode null
sicherer. Ändern Sie die Nullbarkeit so, wie Sie es für die Implementierung von Java-Schnittstellen in Kotlin für gültig halten.
Schauen Sie sich Funktionen an, die bereits hilfreich sein können, z. B. für String?.isNullOrEmpty()
und String?.isNullOrBlank()
die einen nullbaren Wert sicher verarbeiten können, und tun Sie, was Sie erwarten. Tatsächlich können Sie Ihre eigenen Erweiterungen hinzufügen, um Lücken in der Standardbibliothek zu schließen.
Assertion funktioniert wie checkNotNull()
und requireNotNull()
in der Standardbibliothek.
filterNotNull()
Hilfsfunktionen wie das Entfernen von Nullen aus Sammlungen oder das listOfNotNull()
Zurückgeben einer Null- oder Einzelelementliste von einem möglichen null
Wert.
Es gibt auch einen sicheren (nullbaren) Umwandlungsoperator , mit dem eine Umwandlung in einen nicht nullbaren Typ null zurückgeben kann, wenn dies nicht möglich ist. Ich habe jedoch keinen gültigen Anwendungsfall dafür, der mit den anderen oben genannten Methoden nicht gelöst werden kann.