Kotlin vs Java

Especially for Java developers Kotlin is a real enrichment. Not only because Kotlin does (also) run on the JVM and makes accessing existing Java code very easy, but also because it inherited a lot of Java's approved fundamentals, adding a lot of powerful and useful features to it.

Kotlin's creators did not have the burden of staying compatible to previous Java versions and therefore could start from scratch, enabling them to add things to Java which would otherwise not have been possible.

So Kotlin is - at least for me - the Java I had wished for.

On this page I would like to introduce Java developers to the most important and most powerful differences to Kotlin.

No More Semicolons

Let's start with a unremarkable character which feels right at home in Java code which is not imaginable without it - the semicolon ;.

If you are a seasoned Java developer you will be very used to it and you won't even note it anymore. Although, you have to be careful to close (almost) any Java statement properly with it, or let the IDE care about it.

Not so in Kotlin: as long as the (smart) compiler can detect the end of a statement - at a line break, for example - you don't need to write a ;.

Odd at the start, you will get used to not using ; very quickly and ask yourself why it even is required in Java.

No Primitives

In Kotlin, there is no special treatment of Integer versus int, Float versus float, Boolean versus boolean and so on.

The language only knows objects (Int, Float, Boolean,...), no primitives.

So things like (auto-)boxing are nothing to be concerned about in Kotlin.

If you afraid this comes with bad performance, rest assured the Kotlin compiler tries hard to make use of Java's primitives in compiled code whenever possible, for improved performance.

Additionally, with the IntArray class and its siblings, Kotlin offers specialized "primitive" collections for maximum performance.

equals more intuitive

One of the first things you may have (painfully) learned when picking up Java is the fact that the == operator does not actually check objects for equality but directly compares the given references. Instead, Java offers the equals method.

While you finally get used to that, Kotlin reminds you that using == for structual equality (equals) rather than referential equality is the smarter thing to do.

So, in Kotlin, using == will actually internally be converted to a call to equals.

That means, with == you check instances for structual equality rather than for referential equality like in Java.

That's why you won't find any calls to equals in Kotlin code but the much more intuitive and readable == operator.

Finally, if you ever want to check instances for referential equality, Kotlin provides you with the === (3x =) operator to do so.

No Checked Exceptions

In my career, my opinion about "checked or runtime exceptions - which is better?" changed with every new language I learned ;-) I just could not really finally make up my mind, no matter how many articles I read about the topic.

So I am glad Kotlin shares my view I finally got "stuck to" years ago and does not have checked exceptions, only runtime.

No new keyword

In the spirit of avoiding unnecessary code, Kotlin got rid of the new keyword.

Instead, to create a new object you just call the constructor like any other method:

val list = ArrayList()

Kotlin is null-safe

Kotlin tackles the "Billion Dollar Mistake" and finally makes working with null a lot easier and safer.

This starts with the fact, that you have to declare with each and every variable declaration, if it may be null or not.

What might sound like additional hassle, in fact is none, as Kotlin makes it very easy to declare the nullability of a variable, just like be appending a ? to the type (is nullable) or omitting it, making the declarated variable not nullable:

var nullable : String? = "Foo"
var notNullable : String = "Bar"

nullable = null // OK
notNullable = null // Compile time error (as opposed to a NullPointerException at runtime)

val i = notNullable.length // OK
val i = nullable.length // Compile time error: nullable is (could be) null at this moment

if (nullable != null) // check for null
{
    val i=nullable.length // OK! Compiler knows that nullable has already been verified being non-null by the outer if
}

The Kotlin compiler then can check at compile time when a reference potentially can be null, aborting compilation - in contrast to Java, with which the equivalent code would lead to the infamous NullPointerException at runtime.

There are quite a few more useful features dealing with null in Kotlin which I explain further in the Kotlin Guide, and you can read more about in the official documentation about Kotlin's nullability features.

High Order Functions

A cornerstone of Kotlin's power is the support for high order functions. Those are functions which either accept another function as an argument and/or return a (reference to a) function:

fun example(transform: (Int)->Int) = println(transform(42))

example is a high order function, which accepts another function as its sole argument named transform. The accepted function has the signature (Int)->Int, which means it accepts a single Int, noted before the ->, and returns an Int as a result, noted after the ->.

For the purpose of a simple example, our example function just calls the given transform function with 42 as the argument, printing the returned Int.

To actually call example we have to pass it another function matching the (Int)->Int signature as an argument. Such a function might be:

fun square(a: Int) = a * a

Because square is a funtion accepting a single Int and returning one it perfectly fits the requested signature (Int)->Int which example accepts.

So, the following call - which is impossible in Java - becomes possible:

example(::square) // prints `1764` (=42²)

Note the :: operator, which creates a function reference to a declared function, which can be passed on to example as an argument.

To avoid the :: operator and further demonstrate that functions can be "passed around" and handled similar to normal variables, let's hava a look at an alternative approach:

val square=fun (a: Int) = a * a

What we do here is to declare a val named square. But instead of assigning a simple value like 42 or "Hello World!" to it, we assign an actual function to it.

The function is fun (a: Int) = a * a which is very similar to the function declaration before, just laking the name square after fun - it's an anonymous function, so the function itself does not have a name. But as it is immediately assigned to the val square, we can refer to it through that val. Hopefully, the following example makes this clear:

val square=fun (a: Int) = a * a
example(square)

As you can see, we just dropped the :: operator, as we already have a reference to a function in square and don't need the operator anymore.

A Real World Example

Admittedly, my artificial example looks and feels very useless (and it is) but I chose to keep it as simple and as possible. For a real world example, the official documentation does a better job:

fun isOdd(x: Int) = x % 2 != 0
val numbers = listOf(1, 2, 3)
println(numbers.filter(::isOdd)) // prints [1, 3]

The intersting part is the filter function's signature, which looks like this:

public inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T>

This function, being an extension function, generic and inline besides a high order function, was my reason to not start explaining the topic based on it, as you might not yet be familiar with the other concepts.

So, for now, let's just ignore the inline part and the Iterable<T>. name prefix, remove the generics and focus on the argument predicate: (T) -> Boolean making filter a high order function.

public fun filter(predicate: (Int) -> Boolean): List<Int>

Now let's just assume filter was declared in the List class/interface (that's what the extension function part actually does..).

It's purpose would be to filter the lists elements, returning a new list containing only the filtered elements, more precicesly those elements for which the predicate function returns true.

This is a very powerful strategy, and allows to easily filter an existing List with an arbritray filter algorithm. So you can use the very same filter method with any predicate function, making it a very versatile and powerful tool.

Some examples for predicate functions for Ints:

fun isNotNull(x:Int) = x!=null // there actually is a filterNotNull extension function for this common use case
fun isPositive(x: Int)=x>0
fun isNegative(x: Int)=x>0
fun isValidMonth(x:Int) = x in 1..12

The closest resemblance in Java are "functional interfaces", which at least feel a bit like high order functions, but still are far less elegant and powerful.