DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
The Latest "Software Integration: The Intersection of APIs, Microservices, and Cloud-Based Systems" Trend Report
Get the report
  1. DZone
  2. Coding
  3. Languages
  4. Null Safety: Kotlin vs. Java

Null Safety: Kotlin vs. Java

A primer of how null safety in Java compares to that of Kotlin.

Nicolas Fränkel user avatar by
Nicolas Fränkel
CORE ·
Feb. 15, 23 · Tutorial
Like (1)
Save
Tweet
Share
2.85K Views

Join the DZone community and get the full member experience.

Join For Free

Last week, I was at the FOSDEM conference. FOSDEM is specific in that it has multiple rooms, each dedicated to a different theme and organized by a team. I had two talks:

  • Practical Introduction to OpenTelemetry Tracing, in the Monitoring and Observability devroom
  • What I miss in Java, the perspective of a Kotlin developer, in the Friends of OpenJDK devroom

The second talk is from an earlier post. Martin Bonnin did a tweet from a single slide, and it created quite a stir, even attracting Brian Goetz.

In this post, I'd like to expand on the problem of nullability and how it's solved in Kotlin and Java and add my comments to the Twitter thread.

Nullability

I guess that everybody in software development with more than a couple of years of experience has heard the following quote:

I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.

-- Tony Hoare

The basic idea behind null is that one can define an uninitialized variable. If one calls a member of such a variable, the runtime locates the memory address of the variable... and fails to dereference it because there's nothing behind it.

Null values are found in many programming languages under different names:

  • Python has None
  • JavaScript has null
  • So do Java, Scala, and Kotlin
  • Ruby has nil
  • etc.

Some languages do not allow uninitialized values, such as Rust.

Null Safety in Kotlin

As I mentioned, Kotlin does allow null values. However, they are baked into the type system. In Kotlin, every type X has two indeed two types:

  • X, which is non-nullable. No variable of type X can be null. The compiler guarantees it.

    Kotlin
     
    val str: String = null

    The code above won't compile.

  • X?, which is nullable.

    Kotlin
     
    val str: String? = null

    The code above does compile.

If Kotlin allows null values, why do its proponents tout its null safety? The compiler refuses to call members on possible null values, i.e., nullable types.

Kotlin
 
val str: String? = getNullableString()
val int: Int? = str.toIntOrNull()           //1
  1. Doesn't compile

The way to fix the above code is to check whether the variable is null before calling its members:

Kotlin
 
val str: String? = getNullableString()
val int: Int? = if (str == null) null
          else str.toIntOrNull()

The above approach is pretty boilerplate-y, so Kotlin offers the null-safe operator to achieve the same:

Kotlin
 
val str: String? = getNullableString()
val int: Int? = str?.toIntOrNull()

Null Safety in Java

Now that we have described how Kotlin manages null values, it's time to check how Java does it. First, there are neither non-nullable types nor null-safe operators in Java. Thus, every variable can potentially be null and should be considered so.

Java
 
var MyString str = getMyString();           //1
var Integer anInt = null;                   //2
if (str != null) {
    anInt = str.toIntOrNull();
}
  1. String has no toIntOrNull() method, so let's pretend MyString is a wrapper type and delegates to String
  2. A mutable reference is necessary

If you chain multiple calls, it's even worse as every return value can potentially be null. To be on the safe side, we need to check whether the result of each method call is null. The following snippet may throw a NullPointerException:

Java
 
var baz = getFoo().getBar().getBaz();


Here's the fixed but much more verbose version:

Java
 
var foo = getFoo();
var bar = null;
var baz = null;
if (foo != null) {
    bar = foo.getBar();
    if (bar != null) {
        baz = bar.getBaz();
    }
}


For this reason, Java 8 introduced the Optional type. Optional is a wrapper around a possibly null value. Other languages call it Maybe, Option, etc.

Java language's designers advise that a method returns:

  • Type X if X cannot be null
  • Type Optional if X can be null

If we change the return type of all the above methods to Optional, we can rewrite the code in a null-safe way - and get immutability on top:

Java
 
final var baz = getFoo().flatMap(Foo::getBar)
                        .flatMap(Bar::getBaz)
                        .orElse(null);


My main argument regarding this approach is that the Optional itself could be null. The language doesn't guarantee that it's not. Also, it's not advised to use Optional for method input parameters.

To cope with this, annotation-based libraries have popped up:

Project Package Non-null annotation Nullable annotation
JSR 305 javax.annotation @Nonnull @Nullable
Spring org.springframework.lang @NonNull @Nullable
JetBrains org.jetbrains.annotations @NotNull @Nullable
Findbugs edu.umd.cs.findbugs.annotations @NonNull @Nullable
Eclipse org.eclipse.jdt.annotation @NonNull @Nullable
Checker framework org.checkerframework.checker.nullness.qual @NonNull @Nullable
JSpecify org.jspecify @NonNull @Nullable
Lombok org.checkerframework.checker.nullness.qual @NonNull -

However, different libraries work in different ways:

  • Spring produces WARNING messages at compile-time
  • FindBugs requires a dedicated execution
  • Lombok generates code that adds a null check but throws a NullPointerException if it's null anyway
  • etc.

Thanks to Sébastien Deleuze for mentioning JSpecify, which I didn't know previously. It's an industry-wide effort to deal with the current mess. Of course, the famous XKCD comic immediately comes to mind:

How standards proliferate by XKCD

I still hope it will work out!

Conclusion

Java was incepted when null-safety was not a big concern. Hence, NullPointerException occurrences are common. The only safe solution is to wrap every method call in a null check. It works, but it's boilerplate-y and makes the code harder to read.

Multiple alternatives are available, but they have issues: they aren't bulletproof, compete with each other, and work very differently.

Developers praise Kotlin for its null-safety: it's the result of its null-handling mechanism baked into the language design. Java will never be able to compete with Kotlin in this regard, as Java language architects value backward compatibility over code safety. It's their decision, and it's probably a good one when one remembers the pain of migration from Python 2 to Python 3. However, as a developer, it makes Kotlin a much more attractive option than Java to me.

To go further:

  • Are there languages without "null"?
  • Kotlin nullable types and non-null types
  • JSpecify

Originally published at A Java Geek on February 12th, 2023

Java (programming language) Kotlin (programming language)

Published at DZone with permission of Nicolas Fränkel, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Detecting Network Anomalies Using Apache Spark
  • What Is JavaScript Slice? Practical Examples and Guide
  • 4 Best dApp Frameworks for First-Time Ethereum Developers
  • How We Solved an OOM Issue in TiDB with GOMEMLIMIT

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: