7 reasons why Kotlin is the better Java

Ron Veen
Team Rockstars IT
Published in
9 min readJan 10, 2022
Naval cathedral of Saint Nicholas in Kronstadt on the Island Kotlin

Recently, I wrote a story telling about all the changes that were made to the Java language since Java 11. While it proves that the Java language is still developing, it is under pressure from other JVM languages, most notably Kotlin. In this article, I will introduce the most obvious improvements that Kotlin brings to the game

What is Kotlin

If you are reading this article I am assuming that you know what Kotlin is or at least have heard of it. Just to be sure that we are all on the same page, let me give you a short introduction.

Kotlin…

  • …is a statically typed language running on the JVM
  • …takes features from languages like Scala, C#, Groovy, Pascal (and Java of course)
  • …is developed by JetBrains, the company behind IDE’s like IntelliJ, GoLand, RubyMine and AppCode to name a few

The language was designed with a number of goals in mind. It had to be:

  • Concise (Java battles with verbosity)
  • Practical (use modern language constructs)
  • Safe (can we get rid of the dreaded NPE…?)
  • 100% Java interoperable (because, let’s face it, Java is the still the #1 business language out there. Leverage this installed base and try to extend upon it)

While Kotlin was originally developed to run on the JVM, it has since extended it reach to number of other platforms. First of, it is the dominant language for developing Android development, especially since Google has adopted it to be the primary language for Android. But Kotlin did not stop there! Via the Kotlin Native intiative you can build native executables (yes, that’s right, no JVM required!) for Linux, MacOs, Windows (kind of…), iOS (and tvOS and watchOS). We will touch more upon this in a future set of dedicated articles where we will build an application for Linux, MacOs and Windows

After this short introduction let’s introduce the improvements that Kotlin adds on top of the Java language. Please bear in mind that I am talking about language features here, so the things that make life for us as developers easier, nicer and in general more fun.

These features are:

  1. Type inference
  2. Null safety
  3. Properties
  4. Named and default arguments
  5. Data classes
  6. Extension methods
  7. Coroutines

Reason 1: Type inference

Kotlin supports full type inference, both for local variables and for instance variables. It also supports type inference for lamba’s. While Java supports local variable type inference since Java 10 and local variable syntax for lambda parameters since Java 11, no such support exists for instance variables. And arguably that is where need them most. Kotlin supports the full range, as this code snippet shows

Reason 2: Nullability

Tony Hoare was the man who invented the null reference, back in 1965. Many languages adopted the null reference idea, Java among them. Objects having the ability to have a value ‘null’ caused many problems however. Because basically you’re saying: this doesn’t point to anything. Or maybe it does! It’s almost the Schrödingers Cat equivalent of object oriented computing.

If you invoke a method on an object with a null reference (e.g. person.getName() where person contains a null reference) it will crash your application. It causes so many issues even today, that Hoare called his invention of the null reference his “one-billion dollar mistake” (though in all earnest: one billion dollars is a gross underestimation of the actual costs).

In Kotlin, all variables are by default non-nullable, meaning you have to initalize them with a value. A value different to null that is 😉

So all the variables in the snippet above are non-null variables. Trying to set them to null will cause the Kotlin compiler to complain. That’s already much better, isn’t it? It is not blowing up during runtime, but you are already being told during compile time.

But sometimes you cannot get around using nullable variables: You might have to mix Kotlin code with existing Java code. Or you might be using a third party library that is written in Java. So Kotlin lets you define nullable variables, but you have to do so explicitly, thus making yourself admit to your crime.

Postfixing a type with ? marks it as a nullable type. Please note that String and String? are two completely different types to the compiler that bear no resememblance.

If we wanted to invoke a method on these Strings, the compiler would know for sure that it is safe to do so on nonNullableName, as it cannot be null. nullableName on the other hand could contain a null value and invoking a method on it would create havoc. Thus, if you want to so, the compiler wants to make sure that you know what you doing. You have to use a non-null asserted call, also called double-bang call. This is done by postfixing the variable name with double exclamation marks (!!) as such:

Note that line 7 will lead to a Null Pointer Exception (NPE) being thrown, as you willingly invoke a method on an object containing a null value.

To make life a little easier and the code a bit more readable and clean, we can use the safe-call operator (?.)

On line 6, because we use ‘?.’ the uppercase method will never be invoked on nullableName (safe call basically means: only call the method on the object if the object itself has a non-null value). Good work! We prevented an NPE from occuring.

Kotlin has many more methods and lamba’s to easen having to work with nullable values, but they are out of scope for this article.

Best part to remember is that if you are able to use only non-nullable types your code does not need these tricks and no NPEs will occur at runtime.

Reason 3: Properties

We’ve already came across properties in our first example. There we defined ‘name’, ‘age’ and ‘isFemale’. We called them (instance)variables, which actually isn’t quite true: they are properties. What’s the difference? Well, for one they can be defined as being mutable or immutable. And we do not need (but we can) define getters and setters for them.

Defining getters and setters was always very cumbersome in Java and led to a lot of plumbing code. You could have the IDE generate them for you, but that still meant there were a lot of lines of code. And when inspecting such a class you have to wade through all of it to be sure that you understand the complete meaning and nothing unexpected is happening. So people started to use third party tools. In many Java projects, an annotation driven library called ‘Lombok’ has become indispensible. It will generate the bytecode for getters and setters but does not add any source code.

In Kotlin we can directly call the name of the properties to either get or set a value:

The class Person defined in line 11 has a primary constructor that takes one parameter, a String. The constructor keyword can be omitted under certain conditions, more on that later.

Properties in Kotlin do what you would expect, they clean up the clutter and are both concise and practical.

Reason 4: Named and default arguments

Having named arguments is something that I really enjoy in a language. I believe it makes code so much readable. This was a feature I already loved when writing Objective-C code and I am still loving it. It is just something that make me feel good when I look at the code.

Have a look at this example, where I also introduce default values

We defined a method with five parameters from which 3 have been given a default value. What this means is that if you leave these parameters out of the method call they will be assigned their default values instead.

Parameters that have no default value must be specified. When using named arguments their order does not matter, meaning you can change it. Have a look at the method calls in lines 2 and 3 where we switched the order of the first two arguments around. Having named arguments means that you do not need to remember the argument order, meaning it reduces your cognitive load.

Have a look at this example and ask yourself which of these two method calls makes most sense to you.

The answer isn’t very difficult is it? In the second call on line 5 it is obvious which values you are passing to what variables. Even if you have never seen the calculate method you have a good understanding of which values are past and what their meaning is. Add to that the reduced chance of mixing up parameter values and I hope you agree that named arguments are awesome.

Reason 5: Data classes

Conciseness was one of the design goals of Kotlin, as was mentioned above. They Kotlin language designers really outdid themselves when they created data classes. Have a look at this example:

That code in line 10 is all it takes to create a full-fledged class. While it looks a lot like the class we defined under the Properties section above it is a bit different. We added the keyword data in front of the class keyword. A data class will generate for us:

  • The properties as defined in the class definition
  • A default constructor
  • The setter and accessor methods for the properties
  • equals() and it’s friend hashcode()
  • toString() method
  • componentX() methods
  • copy() method

Notice the copy method on line 6. You can specify as many properties as parameters as you want, which makes conditional copying a breeze.

Now you might say that Java has something similar with records. And you would be right. But while records in Java are immutable, kotlin data records are not. And that makes them a perfect match for usage in JPA for instance.

Reason 6: Extension functions

Normally final classes in Java and Kotlin cannot be extended. Basically the compiler says: Here is where it ends, you cannot add any more functions to this class (sealed classes can even be more limiting, both in Kotlin and Java).

Extension functions however, allow you to add any number of new functions to a class, even if the class is final. Let’s look at an example:

Strings are final both in Java and Kotlin. But in line 8 we define an extension function. We are telling that tidyUp should be added as a function to the String class. We invoke the method in line 4, and the tidyUp function will get a reference to the values of current String, input in this example. Thus you can access and manipulate all the available properties in that class.

While extension function are no new phenomenon (C# and Ruby have had them for a long time) they are certainly new to a Java developer. When used properly they are a perfect way to get rid of all these helper and utilityclasses we always have lying around.

Reason 7: Coroutines

Technially coroutines are not a standard part of the language, as the different types of implementations come in their own library and as such are extensible. This means that new variations can be added to language. But let’s not get ahead of ourselves. Lets define what coroutines are first.

It simple terms, coroutines can be considered as lightweight threads, often called green threads, though technically they are not complete alike.

Coroutines are basically state machines that can be suspended when a blocking operation such as network I/O, delay(n) or database operations are encountered. The coroutine scheduler will pick a next coroutine to be executed until that one completes or is suspended itself.

Let’s look at an example of a simple coroutine:

Here we create a simple coroutine that upon creation waits one second and prints its output. Meanwhile, the main thread block due to the runBlocking.

To emphasize that coroutines are really lightweight let us create 100.000 coroutines and execute them:

Note that updating the variable i within a coroutine scope is not thread-safe. This is only done simplicity here. For a better solution look here

This code executes on a MacBook Pro from late 2013 with 16GB of internal memory in 742ms. This shows how little resources this takes.

For functions to be called from coroutines they need to be able to be suspendable. This a achieved by using the keyword suspend. Take a look at this example from JetBrains:

By defining these functions as suspended the Kotlin schedular has the ability to suspended them if a blocking operation occurs.

We have only touched upon the abilities of coroutines in Kotlin and there are many good manuals out there to give you a more comprehensive explanation of what you can achieve with them.

In all fairness it has be mentioned that Java is working on green threads themselves via Project Loom. It used fibres instead of coroutines. Early access builds can be found here.

Summary

Kotlin offers many enhancements over classic Java that help to boost developer productivity and happiness. In my experience, when introducing it to a team, it would take developers around five days to get acquainted and reach the same level of productivity they had in Java. From thereon, they would just start developing faster and with more enjoyment.

The near 100% compatibility with Java makes it extremely easy to mix it into your current Java development, so why not give it a try!

--

--