From Java to Kotlin Part 1: Null Safety
Why Kotlin
Kotlin has been around for 11 years and one question others always ask me is "Why Kotlin? What's so good about Kotlin that makes it so much better than plain Java?" And my answer is always the same, there is no one single major feature which Kotlin offers that makes it superior to Java, rather it is a lot of smaller features. In this series, I'm going to take a look at a couple of those features and compare them to Java to see how each of them solves a particular issue. Even though Kotlin has gained a lot of popularity as "the Android language" it works just as well as a backend language. It integrates nicely with well known frameworks such as Spring Boot, JPA, etc.
Null Safety
One of the first benefits mentioned is always that of 'null safety'. NullPointerExceptions (NPE's) are the bane of many a Java developer's existence. The mere promise of never encountering an NPE again alone will pique a lot of interest. Java does offer some choices to at least try to alleviate the issue, such as Optional
or Nullability annotations. The problem with Optional however is that it isn't meant as a full replacement for nulllable objects. Fields and parameters should never be Optionals, because the Optional itself can still be null and then you haven't really solved anything. Nullability annotations may look like a nice solution, but there is no standard library, you can mix and match annotations from Jetbrains, Spring, Jakarta Validation, Checker framework and more.
Nullability in Kotlin
Kotlin has nullability and non nullability baked into the language. It explicitly discerns between objects which may and may not be null. It uses the ?
type suffix to denote that an object of that type can be null.
// By default, types in Kotlin are non nullable
// This String can't be null
private var str: String = "Hello World!"
// To indicate that an object could be null, we use '?'
private var str: String? = null
So how does this help us really? Well, the compiler checks whether you could have a problem putting nulls into places where they don't belong.
// This does not compile
private var str: String = null
// Given a method like this
fun myFunction(str: String) {}
// We are not allowed to call it like this
myFunction(null)
Ah, but what happens if we do have a null? No problem, Kotlin offers two useful operators, ?.
which returns null if the object a function is called on is null (instead of throwing an exception) and ?:
which returns a default value if it encounters null, let's see them in action.
private var str: String?
// This just returns null if str is null
val length = str?.length
// Note that because of Kotlin's nullability enforcement, this does not compile
val length = str.length
// If we wanted to use a default value instead of null, we could use ?:
val length = str?.length ?: 0
Kotlin simply doesn't allow nullables and non nullables to mingle, it must know which one it is. When you're dealing with return values coming from Java methods, the compiler sometimes can't infer whether they are nullable or not. We can use the !!
operator to tell Kotlin "we're sure that this can't be null, so from now on, treat it as such".
// Assume we get a String here
val str = obj.someMethod()!!
// No ?. needed as we ensured str could not be null
val length = s.length
So what happens if you do get a null from Java-land? Well, you get a NullPointerException again, sorry, but that's what you get when you try to be smarter than the compiler. The !!
operator should only be used when you're absolutely sure you can't get a null.
Kotlin's extension functions (which we're going to explore in more detail in another article) even allow you to define functions on nullable types which could look rather strange to Java developers
// Given the following extension function for nullable strings
fun String?.foo() {}
// This could be called on a nullable string
// Note that we don't need to use ?. here
var str: String? = null
str.foo() // No NPE!
Summary
In this article we looked at how Kotlin deals with nullability. It does this by expanding and splitting the type system into two parts. On one hand, we have all the non nullable types, on the other hand, we have all the nullable types. Kotlin offers useful operators like ?.
, ?:
and !!
to deal with nulls. Because nullability is baked into the language, there is no need for wrapper types such as Optional
or annotations.
Top comments (0)