My FeedDiscussionsHeadless CMS
New
Sign in
Log inSign up
Learn more about Hashnode Headless CMSHashnode Headless CMS
Collaborate seamlessly with Hashnode Headless CMS for Enterprise.
Upgrade ✨Learn more
Why I abandoned Java in favour of Kotlin

Why I abandoned Java in favour of Kotlin

Jan Vladimir Mostert's photo
Jan Vladimir Mostert
·Nov 1, 2016·

11 min read

I’ve always been a huge fan of Java. I started my career as a PHP developer and soon after that I was forced to work in perl in another project. With PHP’s consistent brokenness throughout the language and perl’s (alphabet-soup-puke)-like-syntax, getting into Python two years later was a huge improvement. However, certain things still irked me (which at that time I just couldn’t put into words), but it was there, like a splinter in my hand that would not go away.

Eight months into the Python project, I had to build a system in Java for a freelance project. I had no idea about how to get started, but the more I started messing around with it, the more I loved it. It was like watching poetry unfold and an orchestra perform a software ritual right in front of my eyes. As an ex-Delphi developer, who became accustomed to code that reads almost like English, the ceremony Java provided was beautiful and I was hooked.

Let’s say you are an opera singer performing the same play every day and every night, after some time, you get tired of performing the same ceremony every day and just want to get down to performing - screw the announcements, screw the pre-show drinks, screw the credits, screw the standing ovations you just want to sing, enjoy the singing and not get bogged down by everything else.

For me, the case with Java, was similar. I enjoyed writing code in Java, but all the boilerplate bothered me. You'll see in the next few sections — how then, my intro to Kotlin was a breath of fresh air.


It was quite strange back in the days when Java introduced a 4 letter extension, .java, when every other file-extension at the time was only 3 letters long. Kotlin cuts it down to two letters, .kt, almost as if they're making a statement saying, "we're tired of boilerplate".

Now let's check out some interesting use cases where Kotlin outperforms Java.

Main method

Look at all the ceremony Java gives you before you get to write a single line of code whereas Kotlin gets straight to the performance!

Java:

public class MyClass {
    public static void main(String... args) {
        // code goes here;
    }
}

Kotlin (doesn't even require that main method be in a class):

fun main(args: Array<String>) {
    // code goes here
}

Declaring variables and constants

Java:

private String s1 = "my string 1";
private final String s2 = "my string 2";
private static final String s3 = "my string 3";

Kotlin:

var s1 = "my string 1"
val s2 = "my string 2"
val s3 = """my string 3""" // requires companion object

Also note the lack of semicolons in Kotlin! Semicolons are optional in Kotlin and can be used if you want to write two lines of code on the same line, otherwise you can just ignore it.

Unlike Java which forces you to use double-quotes everywhere, you can use triple quotes in Kotlin! Triple quotes allow you to put anything in there without having to escape strings, let me demonstrate …

Java:

String html = "<div class=\"text\">" + s2 + "</div>";

Kotlin:

val html = """<div class=”text”>$s2</div>"""

Note the $s2, this allows you to place any Kotlin / Java value directly in your String without having to break the string and without having to plus parts of strings, this is really awesome! Alternatively, you can also use ${} which allows you to do things to that value inline, like ${s2.trim()}.

Getters and Setters

Java forces you to use getters and setters everywhere to encapsulate private variables; in Kotlin, anything that is not private, will automatically be exposed via properties making your code more concise and readable - this feature alone got me to try out Kotlin. Furthermore, the null-safety in Kotlin is beyond awesome. If you’re a Java developer, you’ll probably be familiar with endless null-pointer exceptions in your logs or bloated code that does endless null-checks, let me demonstrate with an imaginary HTML class.

Java:

    HTML html = myHelperClass.getHtml();
    String text = "";
    if (html != null){
        Body body = html.getBody();
        if (body != null){
            Div div = body.getDiv();
            if (div.getText() != null){
                text = div.getText();
            }
        }
    }
    System.out.println(text);

Kotlin:

    val html = myHelperClass.html
    val text = html?.body?.div?.text ?: ""
    println(text)

Or in short, in a single line:

    println(myHelperClass.html?.body?.div?.text ?: "")

Notice the lack of getters in Kotlin (getting the job done without the ceremony), the ? does the null check for you and returns null at any point the ? encounters a null and the elvis operator, ?:, checks if the result is null and if so, offers a default value. Null checking in Kotlin is just sooooo much better than Java.

Method extensions

It really closed the deal for me, in Java you have gazillions of helper classes, just think Apache Commons for example or java.utils.Collections, let me give a simple example ...

Java:

public class Helper {
    public static String stripUnwantedSlashes(final String input) {
        if (input == null){
            return null;
        }

        if (input.isEmpty()){
            return "";
        }
        String s = input.replaceAll("\\\\'", "'").replaceAll("\\\\\"", "\"").replaceAll("\\\\`", "`");
        if (s.length() != input.length()){
            return stripUnwantedSlashes(s);
        }
        return s;
    }
}

Now every time you want to make use of this helper function, you have to do use the Helper class which bloats your code pretty quickly.

System.out.println(Helper.stripUnwantedSlashes(text));

Kotlin fixes this by allowing you to extend the String class injecting your own method. Note that I don’t need to do a null check on input, by defining it as type String, the compiler won't allow it to be null; it won't even compile if it can be null. If you want to allow null, you need to define the type as String? instead and handle the null, otherwise, once again, it won’t even compile.

Kotlin:

fun String.stripUnwantedSlashes() : String {
    if (this.isEmpty()){
        return ""
    }
    val s = this.replace("\\\\'".toRegex(), "'").replace(("\\\\\"").toRegex(), "\"").replace(("\\\\`").toRegex(), "`")
    if (s.length != this.length){
        return s.stripUnwantedSlashes()
    }
    return s
}    

See the difference, no Helper. bloat:

println(text?.stripUnwantedSlashes() ?: "");

So instead of doing Collections.sort(mylist);, you can get straight down to business and just do mylist.sort() Instead of bloating your code with Integer.parseInt("1"), get straight down to business and do "1".toInt()

Smart casting in Kotlin

This is another awesome feature that you don’t get in Java!

Following is a Java class that we'll extend, and use in both Java and Kotlin:

public abstract class Param<T> {

    private T value;

    public Param(T value) {
        this.value = value;
    }

    public T getValue() {
        return value;
    }

}

public class Password extends Param<String> {
    public Password(String value) {
        super(value);
    }

    public String getMaskedValue() {
        // code that masks password and returns it
    }
}

public class Username extends Param<String> {
    public Username(String value) {
        super(value);
    }
}

Java:

    List<Param> params = Arrays.asList(new Password("password1"), new Password("password2"), new Username("dude"));
    params.forEach((param) -> {
        if (param instanceof Password){
            System.out.println("Password=" + ((Password) param).getMaskedValue());
        } else if (param instanceof Username){
            System.out.println("Username=" + ((Username) param).getValue());
        }
    });

Kotlin:

val params = mutableListOf(Password("password1"), Password("password2"), Username("dude"))
params.forEach { param ->
    if (param is Password){
        println("Password=${param.maskedValue}")
    } else if (param is Username){
        println("Username=${param.value}")
    }
}

Notice how in the Java code, you need to cast your param every time you want to use it! Kotlin on the other hand, once you've confirmed param's type, you're free to use it as if it is of that type!

Kotlin also allows you to tell it what type param is in which case you can also just use it as if it is of that type. Example, let's assume that list only contained Password objects

Kotlin:

params.forEach { param -> 
    param as Password
    println(param.maskedValue)
    println(param.maskedValue)
    println(param.maskedValue)
}

Primary Constructors and named arguments

Another awesome feature I wish Java had, is the way you can more precisely call methods and constructors with named arguments.

Java:

Java, defining a class, typically looks like this:

public class User {

    private String firstName;
    private String lastName;
    private String email;

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

}

To make use of this class in Java, you'll typically write this boilerplate:

    User user = new User();
    user.setFirstName("Graham");
    user.setLastName("Spencer");

Kotlin:

Kotlin allows you to firstly get rid of the getters and setters, but also allows you to set variables in your class directly from the constructor

class User(
        var firstName: String = "",
        var lastName: String = "",
        var email: String = "") {

    override fun toString(): String {
        return "FirstName=${firstName}, LastName=${lastName}, Email=${email}"
    }

}

To make use of this class in Kotlin, you can either do what Java did and set the variables individually, or you can passs the variables into the constructor or you can make use of named parameters (the one which I prefer most)

Kotlin (option1):

    val user1 = User()
    user1.firstName = "Graham"
    user1.lastName = "Spencer"

Kotlin (option2) (notice that we can skip the last parameter since it has a default value):

    val user2 = User("Graham", "Spencer")

Kotlin (option3) (notice that we can now pass in the parameters in any order):

    var user3 = User(lastName = "Spencer", firstName = "Graham")

Methods in Kotlin also allows you to either call them via sequential arguments, or named arguments. The latter is great for when you want to make a method call much more readable, example, which looks better?

findUser("Graham", "Spencer", "0841234567", 33, 40, "Mr.")

or

findUser(
        title = "Mr", firstName = "Graham", lastName = "Spencer", 
        phoneNumber = "0841234567", minAge = 33, maxAge = 40)

Using named parameters, you can even omit parameters that does not make sense to provide (assuming you provide default values for them in the method declaration) instead of providing empty strings or null. How many times have you seen long strings of null being sent into a method and not even knowing if the method will handle it correctly?

Java:

findUser(null, null, null, 33, 40, "Mr");

Kotlin:

findUser(title="Mr", minAge=33, maxAge=40)

Operator overloading

This is something Kotlin has which Java does not! There’s been many attempts to get operator overloading into Java, but the founders seems to kick back against it. Operator overloading is a feature I enjoyed very much in Python and I’m happy to see that Kotlin has it as well.

Instead of writing a.contains(b) in Java, you can overload the contains method or use method extensions to inject a contains method which will allow you to write Kotlin a in b And this can be done for any operator that you can apply on an integer, like +, -, ++, etc

Kotlin:

Let's override the compareTo operator in User:

operator fun compareTo(user: User): Int {
    return firstName.compareTo(user.firstName)
}

Now which one reads easier?

if (user1.compareTo(user2) > 0)

or

if (user1 > user2)

Better Switch Case

Haven't you always wished for a switch case that is a little more powerful? Java’s switch case is really simplistic, Kotlin gives you when which allows you to do anything an if-else can do, without the boilerplate.

Kotlin:

when (x) {
    1 -> println("x == 1")
    2 -> println("x == 2")
    3,4 -> {
        println("x == 3")
        println("x == 4")
    }
    in 5..10 -> println("x >= 5 and x <= 10")
    is String -> println("x is actually a string")
    else -> {
        println("this is the else block")
    }
}

The first question I asked on Kotlin's Slack channel (which is very active by the way) was about whether Kotlin offered something similar to Dart's double-dot operator and some guy told me that I can use DSLs to write my own .. syntax, but he would suggest I stick to with. With allows you to switch the scope, let me demonstrate.

with (user1){
    firstName = "Graham"
    lastName = "Spencer"
    println("First=${firstName}, Last=${lastName}")
    if (this > user2){
        println("user1 > user2")
    }
}

Without with, you'd have to type user1 out every time and if you had nested properties, you'd have to chain user1.a.b.c.d.e them which reads very hard when you're integrating into complex APIs with many levels of nested objects.

    user1.firstName = "Graham"
    user1.lastName = "Spencer"
    println("First=${user1.firstName}, Last=${user1.lastName}")
    if (user1 > user2){
        println("user1 > user2")
    }

Getting rid of equals()

I think the biggest win for Kotlin is getting rid of having to use equals everywhere!!!

In Java, when using == on objects (even Strings), you are comparing pointers, something which I've never seen anyone use in any real-world code and instead you are forced to write string1.equals(string2) everywhere.

Kotlin decided that since hardly anyone ever wants to compare pointers, why not make == actually compare values and instead, if you REALLY want to compare pointers, use ===.

So instead of user1.firstName.equals(user2.firstName), you can write user1.firstName == user2.firstName.


To existing Kotlin users, I know I know, I'm just shaving the tip of the iceberg here. There's plenty more to cover, but to cut a long story short, if you're already a Scala developer and are liking it, you'll probably not find many new things in Kotlin that you don't have already (except for the zero-overhead null-safety). But if you're a Java developer and are tired of the boilerplate and all the ceremonies before you get to do any real work, then Kotlin is for you.

Kotlin is literally a better Java and I've already said goodbye to Java many months ago, not writing any new code in Java. For existing Java projects, I simply continue writing all new code in Kotlin while keeping the old code in Java, and keeping them both in the same project.

Cheers to the Kotlin team for making Java better!