Handling null

One of the biggest differences between Kotlin and Java is the handling of null types. Because of this, it is one part of the interop story that must be given careful thought. Thankfully, our tools can help make this easier.

To explore the handling of null across language boundaries, we'll, once again, return to the Person class. In our Person example, both properties are defined as non-null String types:

open class Person(val firstName: String, val lastName: String)

Because the Kotlin compiler knows these values will not be null, we can safely reference them without any type of null check, as shown in the following snippet:

person.firstName.length

However, when we access those properties from Java, we must store them as nullable types, because that's all that Java has:

// firstName is now a nullable type
String firstName = person.getFirstName();

At this point, we can't be sure that the value returned from getFirstName() is non-null, because there's no way for Java to enforce that. In the following code, we access the firstName variable without any type of null check, which could lead to potential issues:

String firstName = person.getFirstName();
firstName.length();

To protect properly against null access in Java, we must rely on traditional strategies of Java development, such as checking for null before accessing a variable:

String firstName = person.getFirstName();
if (firstName != null) {
// handle non-null value
}

While the Java type system might result in the loss of strict typing information present in Kotlin, the available tooling can help us understand whether a variable might be nullable. IntelliJ-based IDEs can analyze the code in an attempt to understand when Java fields and variables may be non-null. In this example, IntelliJ is able to inform us that the firstName variable will not be null since Person.firstName is a non-null property:

// firstName is now a nullable type
String firstName = person.getFirstName();
if (firstName != null) { // condition is always true
// handle non-null value
}

While the inspection warning is useful, it's not something strictly enforced by the compiler, so you need to be aware of when and where you can correctly infer nullability across the languages, and where you still need to perform explicit type checks.

Another example of this is passing parameters to constructors or functions written in Kotlin. This snippet illustrates the issue:

// Passing a null value when instantiating from Java
new
Person(null, "Smith"); // compiles but will result in error

This code will compile even though both properties of the Person class are non-null. From Java, there isn't any way to strictly enforce the rule that the passed values should be non-null. In this case, IllegalArgumentException will be thrown indicating that the passed parameter must be non-null. The IDE can help in these situations as well by providing inspections that warn if a potentially null value is passed where a non-null value is expected, but it's up to you to ensure that the code handles these situations correctly since the compiler can't enforce it.

Another area of Kotlin where interop code from Java becomes potentially more verbose is function arguments. In the next section, we'll take a look at how Kotlin features such as named arguments and default parameter values can and can not be used from Java.