1. Overview

In this tutorial, we’ll define Kotlin’s term, platform types. Afterward, we’ll show how to handle platform types. Finally, we’ll demonstrate Java annotations supporting nullability checks in Kotlin.

2. Platform Types Definition

Let’s first explain what platform types are. In short, platform types are types of Java declaration. Objects in the Java language are nullable. Therefore, Kotlin can’t check their nullability at compile-time in Kotlin. Additionally, Kotlin’s compiler is not able to issue an error. Moreover, platform types may cause an error at runtime. Therefore, Kotlin’s paradigm of strict null-safety is broken.

The notation of platform types is defined as a type with an exclamation mark. For example, String! means that object may be nullable or not.

3. Nullability Check for Platform Types

Let’s have a look at how we can handle the nullability of platform types. First, let’s define a Client class:

public class Client {
    private String name;

    public Client() {
    }

    public String getName() {
        return name;
    }
}

The client’s name is nullable. Additionally, let’s test what happens when we call the getName method:

@Test
fun givenNullable_whenCall_thenFail() {
    val client = Client()
    assertFailsWith(NullPointerException::class) {
        client.name.length
    }
}

First, there’s no compilation error alerting us that name is nullable. Moreover, the test runs successfully. It throws the NullPointerException because the field name equals null.

By default, Kotlin treats platform types as not nullable. However, we can change this by defining the nullable reference type:

@Test
fun givenNullable_whenCall_thenNotFail() {
    val client = Client()
    val name: String? = client.name
    assertThat(name?.length).isNull()
}

After that, thanks to the safe call on the name object, we handled its nullability.

4. Java Annotations Supporting Nullability Check

Let’s now have a look at how we can provide Java’s object nullability to Kotlin. Java types that provide nullability information are not platform types. Therefore, Kotlin’s compiler throws an error when we access nullable objects without the safe call.

The list of supported annotations includes:

  • JetBrains – @Nullable and @NotNull from the org.jetbrains.annotations package
  • JSpecify – org.jspecify.nullness
  • Android – com.android.annotations and android.support.annotations
  • JSR-305 – javax.annotation
  • FindBugs – edu.umd.cs.findbugs.annotations
  • Eclipse – org.eclipse.jdt.annotation
  • Lombok – lombok.NonNull
  • RxJava 3 – io.reactivex.rxjava3.annotations

Moreover, the class JvmAnnotationNames contains a complete list of supported annotations. Additionally, we can specify which annotations must be reported and how by the compiler. The compiler option -Xnullability-annotations=@<package-name>:<report-level> defines annotation package and reporting level.

The level may be one of:

  • ignore – to ignore nullability mismatch
  • warn – to warn about nullability mismatch
  • strict – to throw an error on nullability mismatch

Let’s now modify the Client class by adding the @Nullable annotation to the exposed getter:

@Nullable
public String getName() {
    return name;
}

After that, we won’t be able to run the test givenNullable_whenCall_thenFail. The compiler throws an exception with an error message:

Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String?

Thanks to the annotation, Kotlin’s compiler knows that getName may return null.

5. Conclusion

In this short article, we explained platform types in Kotlin. Additionally, we showed how to handle the nullability of platform types. Finally, we reviewed annotations that support the nullability check in Kotlin.

As always, the source code of the examples is available over on GitHub.

Comments are closed on this article!