Jailhouse Rock
@NonNull und @Nullable in Kotlin
Ein Hauptaugenmerk von Kotlin im Unterschied zu Java dürfte auf dieser ganz bestimmten Eigenschaft liegen: Der Null- (bzw. Nicht-Null-) Sicherheit. Das bedeutet: Jede Referenz in Kotlin muss bereits bei der Definition gesetzt werden und wird im Compiler geprüft. Das heißt dann wohl: Adieu, NPE und Null-checks… ?
val darfNullSein: Object // so nicht erlaubt!
val darfNullSein = "" // In diesem Fall wird der Typ automatisch als String gesetzt.
Man kann natürlich zulassen, dass dennoch null-Werte gesetzt werden, wenn man es so definiert, doch auch das muss explizit zugewiesen werden:
val darfNullSein: Object? = null // entspricht Java: “public @Nullable Object darfNullSein = null;”
Möchte man dieses object später im Code verwenden, muß man seine Methoden mit einem zusätzlichen Fragezeichen vor dem Punkt aufrufen:
darfNullSein?.toString() // in Java: “darfNullSein == null ? null : darfNullSein.toString();”
Damit wird automatisch ein Null-Check durchgeführt. Ist object == null, wird null zurückgegeben, ansonsten das Ergebnis des Methodenaufrufs. Dies läßt sich auch beliebig verketten:
darfNullSein?.method1()?.method2()?.method3()?. …
Mit dem sogenannten “Elvis-Operator” ?:
kann auch ein anderer Standardwert als null
zurückgegeben werden. (Wer die Referenz nicht gleich versteht: einfach den Operator als Emoticon lesen und die Haartolle bewundern …)
darfNullSein?.toString() ?: ""
Eine radikalere Methode stellt der folgende Aufruf dar:
darfNullSein!!.toString() // vgl. Java: “Objects.requireNonNull(darfNullSein);”
Dieser führt dazu, daß das Objekt an dieser Stelle nicht!! null!! sein!! darf!! Ist es das trotzdem, dann wird doch noch die berüchtigte NullPointerException freigelassen und das Programm bricht ab.
Zuletzt gibt es noch die Möglichkeit, die Zuweisung einer Variablen aufzuschieben, falls während des Konstruktors noch nicht alle Voraussetzungen erfüllt sind und sie erst später im Lebenszyklus der Klasse gesetzt werden kann. Dafür gibt es das Schlüsselwort lateinit
. Das funktioniert allerdings nicht mit Immutables, d.h. nur bei var
, nicht .val
lateinit var wirdSpäterGefüllt: Object // darf nicht null sein
Spätestens ab hier ist man dann selbst dafür verantwortlich, dass die Initialisierung noch vor dem ersten Aufruf geschieht.