Kotlin has strict patterns around nullness when compared to languages like Java. This is a boon when working exclusively in Kotlin, but how is this pattern implemented? Do Kotlin’s rules still protect you when interoperating with a less strict language like Java? Think back to the printPlayerStatus function from Chapter 4.
fun printPlayerStatus(auraColor: String, isBlessed: Boolean, name: String, healthStatus: String) { ... }
printPlayerStatus takes in parameters of Kotlin types String and Boolean.
If you are calling this function from Kotlin, then the function signature is clear – auraColor, name, and healthStatus must be of type String, which is not nullable, and isBlessed must be of type Boolean, which is also not nullable. However, because Java does not have the same rules regarding nullability, a String in Java could potentially be null.
How does Kotlin maintain a null-safe environment? Answering that question requires a dive into the decompiled Java bytecode:
public static final void printPlayerStatus(@NotNull String auraColor, boolean isBlessed, @NotNull String name, @NotNull String healthStatus) { Intrinsics.checkParameterIsNotNull(auraColor, "auraColor"); Intrinsics.checkParameterIsNotNull(name, "name"); Intrinsics.checkParameterIsNotNull(healthStatus, "healthStatus"); ... }
There are two mechanisms for ensuring that non-null parameters do not accept null arguments.
First, note the @NotNull
annotations on each of the non-primitive parameters to printPlayerStatus.
These annotations serve as a signal to callers of this Java method that the annotated parameters should not take null arguments.
isBlessed does not require a @NotNull
annotation, because booleans are represented as primitive types in Java and, as such, cannot be null.
@NotNull
annotations can be found in many Java projects, but they are particularly useful for those calling Java methods from Kotlin,
as the Kotlin compiler uses them to determine whether a Java method parameter is nullable.
You will learn more about Kotlin’s interoperability with Java in Chapter 20.
The Kotlin compiler goes a step further in guaranteeing that auraColor, name, and healthStatus will not be null: using a method called Intrinsics.checkParameterIsNotNull. This method is called on each non-nullable parameter and will throw an IllegalArgumentException if a null value manages to be passed as an argument.
In short, any function that you declare in Kotlin will play by Kotlin’s rules about nullness, even when represented as Java code on the JVM.
So there you have it – you are doubly protected from a null pointer exception when writing functions that take values of non-null types in Kotlin, even when interoperating with languages that are less strict about nullness.