Published on

Can you 'speak' Kotlin

Authors
kevin-364843-002

It is always exciting to learn a new language. I was looking through a new code base to try and familiarise myself with how everything fitted together, when I stumbled on a file with a .kt extension. The code in the file felt very similar to Java and my IDE was syntax highlighting everything alright, yet it was still not the Java I was used to. At first I thought it was Java 9, but after a quick Google I was pleasantly surprised to discover that it was Kotlin. I was hooked in no time. Ever since I’ve made this discovery, I have been trying to find every excuse I can to introduce more Kotlin into the code base.

The project I am currently working on is a Spring based Java/Kotlin web app with an Angular front end. In this context here are some of my experiences with the language that have really drawn me to it as well as some of the things that tripped me up.

It is a JVM language

Kotlin files look very familiar if you are coming from a predominantly Java background. It is a JVM language which can be used interchangeably with Java, so it has access to the enormous Java ecosystem as well as the burgeoning Kotlin one. If you come from a Java background it has a much smaller learning curve.

It fits very easily into existing Java projects as it has Maven and Gradle plug-ins to convert Kotlin to Java at build time. It also converts Kotlin to Java 6, so you are not blocked from using Kotlin if you are stuck on an older version of Java.

If you use IntelliJ or Android Studio (as it is also developed by JetBrains) then Kotlin really has excellent integration into their IDEs. For instance, it is super simple to convert existing Java code to Kotlin – simply push Cmd/Ctrl+Alt+k in any Java file and the IDE converts it to the Kotlin equivalent (just be careful as you have to manually convert it back or use your VCS if you decide you want the Java file back).

When you convert your first Java class to Kotlin, the IDE will ask you if you want it to add the Kotlin plug-in to your Maven/Gradle file which fully integrates it into your code base. This conversion feature was a huge help in learning how Kotlin worked and fitted together. I found this feature most useful in easing into the language at my own pace, by coding something that I was familiar with in Java and then converting it to Kotlin to understand how it mapped to Kotlin.

Variables are not nullable by default

In Java there is no language level way of preventing a variable from being set to null other than explicitly coding in checks for it. This means you have to design your code defensively to deal with nulls, and even then, null related errors still pop up.

In Kotlin the concept of nullable variables is baked into the language resulting in compile time errors for non-nullable variables you try to assign to null. The language even takes it one step further by giving you compile time errors when trying to access properties on fields that are nullable. This is made possible without having done the appropriate checks or using the correct operators to explicitly handle nulls. This is incredibly powerful as you want errors to crop up as early and prominently as possible in a system. The later they appear, the more difficult and costly they are to fix.

To illustrate this feature let’s first look at an example of some code in Java where you want to store a person’s name and later get the length of it:

String name = "John";
...
/* somewhere in your code you assign this a null value */
name = null;
...
/* somewhere else you want to now assign the length of this to a new field */
int nameLength = name.length;

This will compile in Java without issues, but give you a NullPointerException when you run the code. If this code is not properly tested and buried deep under a bunch of logic, it may end up cropping up at the worst time – only after the code has been humming away in production for a while, by which time its developer has long forgotten about it.

In Kotlin we would write this same code like this:

var name = "John"
...
/* somewhere in your code you assign this a null value */
name = null
...
/* somewhere else you want to now assign the length of this to a new field */
var nameLength = name.length

The difference here is that this code will not even compile and your IDE will give you an error immediately saying something like: Null cannot be a value of non-null type String

To fix this issue in Java, we would do something like:

String name = "John";
...
/* somewhere in your code you assign this a null value */
name = null
...
/* somewhere else you want to now assign the length of this to a new field */
int nameLength = 0;
if(name != null){
    nameLength = name.length;
}

In Kotlin on the other hand, we would fix this compile error as follows:

var name: String? = "John"
...
/* somewhere in your code you assign this a null value */
name = null
...
/* somewhere else you want to now assign the length of this to a new field */
var nameLength = name?.length ?: 0

In this code I had to declare the type of the name variable as String? (note the ?) which is how you indicate in Kotlin that a variable may be assigned a null value.

What is interesting about this example is that if I tried to assign nameLength to name.length (note the absence of a ?) it would also have generated a compiler error. The reason for this is that Kotlin keeps track of which variables are nullable and checks at compile time to confirm that you have handled this nullable by explicitly checking the way we did in the Java code – using the elvis operator as we did above or by using the !! operator. This is where you are basically saying: “Trust me I’m a developer” (this would result in a NullPointerException being thrown if name ends up being null at runtime but when reading the code that is blatantly clear because of the use of the !! operator).

Immutable vs Mutable

One of the things that functional languages push is immutability and the use of pure functions (the same input to the function results in the same output and the function has no side effects). This helps to make code less error prone as there is less chance of side effects. It is also easier to run the code in parallel as you do not have to worry about changing state.

Kotlin makes coding to this philosophy much easier by simplifying the way we make a variable immutable. In Java this of course can also be done by using the final keyword when declaring a variable, but that takes more conscious effort. In most of the production codebases I have encountered, this is mainly done in beans with constructor injection and not in very many other places. The Java approach involves a much wordier declaration of the variable which may be part of the reason that it is not being used as often.

In Kotlin you have to declare a variable in one of two ways, as either: a var or a val. Declaring a variable as a var indicates that the variable is mutable and can be reassigned. val on the other hand indicates that the variable is immutable. This is the equivalent of let vs const (respectively) in JavaScript.

Kotlin’s variable declaration approach makes spotting immutable variables in a file much easier. If you are using IntelliJ, the IDE will suggest variables that can be converted to their immutable versions by switching it to use the val keyword.

You can even conditionally assign a variable and make it immutable by using a special kind of Kotlin if syntax which again the IDE often can do for you using code assist (this is the equivalent of a ternary operator):

val foo = if (bar) {
    "fizz"
} else {
    "buzz"
}

Kotlin also has versions of the most common collections that are either explicitly mutable or immutable. The default syntax to create instances of these collections uses the immutable version. Immutable collections are possible in Java, but they need boilerplate to do this such as the Collections class to wrap mutable collections and make them immutable.

Very similar syntactically to TypeScript

If you use TypeScript on the front-end, Kotlin has a very similar feel. For example TypeScript also declares variable names first and then the variable type. Variables can be declared without having to explicitly define the type. You may define the type if you wish, even if the type can be inferred from the declaration.

Far less boilerplate

The whole point of code is to deliver business value. Code is a necessary evil and the more of it you have, the more of it you have to maintain and the higher chance there is of errors. Kotlin can help in this regard by cutting down the swaths of boilerplate there is in Java and in so doing, make your code more succinct, readable and maintainable.

IntelliJ makes it very easy to convert existing Java code to Kotlin. By doing this, you will realise just how much boilerplate code and frivolous syntactic words there are in Java. For example, in Kotlin you can end statements with semicolons but it’s perfectly acceptable and encouraged by the IDE to leave them out.

Declaring beans is a really good use case for Kotlin. It is the one area of our project where Kotlin has become mandatory. The reason for this is that you can declare the average Java bean in about two lines of Kotlin.

For example, say I needed to create a bean that would hold a person’s name and surname, we would do something like the following:

public class Person {
       private final String name;
       private final String surname;

        public Person(String name, String surname) {
          this.name = name;
          this.surname = surname;
        }
        public String getName() {
           return name;
        }
        public String getSurname() {
          return surname;
        }
}

The Kotlin equivalent would be:

class Person(val name: String, val surname: String)

You might say: “Nah that is really not a big deal. My IDE does most of the heavy lifting for me when generating a bean like that!”. Which is true, but as a developer you spend the majority of your time reading code instead of writing it. This means that the more Java beans like that you have, the more time you have to spend sifting through and reasoning about code.

The Kotlin example is much more succinct and to the point. You can read and modify it significantly faster. There is significantly less chance of changing something small and breaking something else in the process as there is less code to scan through so less code to debug.

Another really cool feature of Kotlin is if you wanted to introduce a toString, equals, hashCode and the usual bells and whistles into your bean, you simply would have to prefix your class with the data keyword (see the example below). This also allows the bean’s fields to be destructured which can make accessing fields much simpler.

data class Person(val name: String, val surname: String)

In a similar vein to this, if a bean implements comparable, Kotlin will automatically know to convert the below:

[1] object1.compareTo(object2) > 0
[2] object1.compareTo(object2) == 0
[3] object1.compareTo(object2) <= 0

to

[1] object1 > object2
[2] object1 == object2
[3] object1 <= object2

This again further enhances the readability of the code and cuts down more unnecessary syntax.

Other common tasks like declaring sets, lists or maps are much easier in Kotlin and can generally be done in-line further reducing unnecessary code:

val people = listOf("John", "Jane", "Sally")
val numbersToWords = mapOf(1 to "One", 2 to "Two")

Some pitfalls with the language

Like any language, Kotlin has pitfalls. The main one I stumbled on is that classes declared in Kotlin are final by default (the previous bean example is a final class which cannot be extended unless it is declared as open). This can be a problem if you are using a framework like Spring which proxies classes by extending them. Luckily issues like these are easy to overcome by using plug-ins like the all-open plug-in or declaring classes that you want proxied as open.

I have also found it a pain to use mocking frameworks like Mockito where I want to mock a method to allow input parameters of any type or of a specific type. I have not found any easy solution to solve this, other than explicitly specifying the exact parameter field to expect or writing the test in Java instead.

I have also ran into issues with Jackson where some Kotlin beans are not marshalled correctly for beans with fields that have none-standard names. I ended up converting these beans back to Java to get around this.

If you use IntelliJ the IDE constantly keeps prompting you to upgrade Kotlin. This is annoying as there is no way to disable this as far as I can see. Constantly upgrading a production codebase’s language is the last thing you want to do as breaking changes or even minor changes can result in unforeseen bugs in your production code.

Conclusion

I have only scratched the surface of what Kotlin is capable of. As a language that is being actively worked on at a rapid tempo, the list of cool features will continue to increase. That is one of the things I like best about Kotlin: there are constantly new and more elegant ways of doing things using different features of the language and in so doing simplifying development. I believe Kotlin is a perfect fit for development environments where Java is used, if anything for its ability to make programming easier, code more succinct and readable.

This blog is a cross post, it was originally posted here.