One of the most common pitfalls in Java programming is accessing a member of a null reference, that results in a NullPointerException, because virtually any reference in a Java program can hold null. It happens so often that we have a casual abbreviation for it: NPE. Also have a look at this talk: "The Billion Dollar Mistake".
Kotlin's type system is aimed to eliminate NullPointerException's from our code. The only possible causes of NPE's may be
- Someone had explicitly thrown is (throw NullPointerException())
- Someone called external Java code that caused it
- There's data inconsistency w.r.t initialization (an uninitialized this available in a constructor is used somewhere)
In Kotlin the type system distinguishes between references that can hold null (nullable references) and those that can not (non-null references). For example, a regular variable of type String can not hold null:
To allow nulls, one declares a variable as nullable string, written String?:
Now, if you call a method on a, it's guaranteed not to cause an NPE, so you can safely say
But we still need to call that method, right? There are a few ways of doing that.
First, you can explicitly check if b is null, and handle the two options separately:
The compiler tracks the information about the check you performed, and allows the call to length() inside the if. More complex conditions are supported as well:
Note that this only works where b is immutable (i.e. a local val or a member val which has a backing field and is not overridable), because otherwise it might happen that b changes to null after the check.
Your second option is the safe call operator, written ?.:
This returns b.length() if b is not null, and null otherwise. The type of this expression is Int?.
Safe calls are useful in chains. For example, if Bob, an Employee, may be assigned to a Department (or not), that in turn may have another Employee as a department head, then to obtain the name of Bob's department head, if any), we write the following:
Such a chain returns null if any of the properties in it is null.
When we have a nullable reference r, we can say "if r is not null, use it, otherwise use some non-null value x":
Along with the complete if expression, this can be expressed with the Elvis operator, written ?::
If the expression to the left of ?: is not null, the elvis operator returns it, otherwise it returns the expression to the right. Note that the right-hand side expression is evaluated only if the left-hand side is null.
The third option is for NPE-lovers. One can write b!!, and this will return a non-null value of b (e.g., a String in our example) or throw an NPE if b is null:
Thus, if you want an NPE, you can have it, but you have to ask for it explicitly, and it does not appear out of the blue.
By the way, !! is added for conciseness, and formerly was emulated by an extension function from the standard library, defined as follows:
Regular casts may result into a ClassCastException if the object is not of the target type. Another option is to use safe casts that return null is the attempt was not successful:
J. Bloch, Effective Java. Second Edition
Item 38: Check parameters for validity
Item 43: Return empty arrays or collections, not nulls
In Java, one can use annotations on methods and parameters (or on types, when JSR-308 is accepted), these annotations can be used by external tools to verify null-safety. For example, one can use the @NotNull annotation supported by IntelliJ IDEA.
Scala promotes using the Option type to control absent values. This introduces run-time overhead because values are wrapped into objects of type Option, and the syntax is a little verbose.
C# has Nullable types that boil down to wrapping references into value types allocated on the stack, and there's no actual static checks that would prohibit calling methods on a null reference.
Gosu's approach is close to ours.