Fri, 10 Feb 2017 by Thomas ten Cate · Comments

programming

Kotlin is a programming language developed by JetBrains (the makers of IntelliJ IDEA), which compiles down to Java bytecode. I got over my initial aversion for the ugly name, and decided to give it a try. Now I never want to go back to Java. Here’s why.

1. Full Java interoperability

Kotlin was explicitly designed as a replacement for Java. That means your Kotlin classes can inherit from Java classes, implement Java interfaces, call Java methods, and so on. Conversely, Java code can inherit from Kotlin classes, implement Kotlin interfaces and call Kotlin methods. It feels like you’re writing Java, but without the straitjacket.

If you use IntelliJ IDEA (which you should), it gets better. You can copy Java code and paste it into a Kotlin file, and IntelliJ will offer to translate it to Kotlin for you! I’m sure there’s a way to mass-convert an entire project, but I haven’t needed it yet. The translation isn’t perfect, but the places where it fails result in compile errors; I haven’t seen cases where any shortcomings would only have become apparent at runtime.

For me, this means that I can continue to use my favourite game development framework, LibGDX, without the pain that is Java (especially before Java 8).

And did I mention you can use Kotlin for Android development too?

2. Extension functions

Similar to C#, in Kotlin you can define methods on a class while not actually writing them inside the class:

class Person { var firstName = ... var lastName = ... } // Maybe the author of the Person class forgot to include this fun Person.setName(firstName: String, lastName: String) { this.firstName = firstName this.lastName = lastName }

This is incredibly useful if you’re working with a lot of library code that you cannot change, but it’s also nice to keep the actual classes small and easy to reason about. An extension function does not have access to private fields of the class it’s defined on, so it cannot violate the invariants that the class’s author had in mind.

I must add here that you probably don’t want to go overboard with extension methods, unless you have a good IDE (did I mention IntelliJ?). Otherwise, it can get hard to tell where all these methods are coming from. Keep them close together, in the same file as the class if you can.

3. Receiver functions

I think this is my favourite Kotlin feature overall. The only thing I know that’s similar are Ruby’s blocks. Basically, Kotlin lets you declare higher-order functions that are functions on a particular type. That probably sounds confusing, so here’s an example:

fun table(init: Table.() -> Unit): Table { val table = Table() table.init() return table } fun Table.row(init: Row.() -> Unit) { val row = Row() row.init() this.add(row) } fun Row.cell(text: String) { this.add(Cell(text)) }

Note the signature of Table.row ? init is of type Row.() -> Unit . That means: a function that can be called on a Row object, and returns Unit (i.e. nothing). Within that function, this will refer to the Row .

With the code above, we have defined our own little DSL (domain-specific language)! Observe:

val myTable = table { row { cell("top left") cell("top right") } row { cell("bottom left") cell("bottom right") } }

That works thanks to another small but important syntax rule in Kotlin: if the last argument to a function is itself a function, you can put it outside the parentheses. The Kotlin website has a more elaborate example, though I’m not sure about their shameless abuse of + operator overloading.

And note that this is entirely type-safe: you can’t call cell outside the context of a Row , for example.

4. Inline functions

Don’t all these fancy higher-order constructs come at a runtime cost? Not if you inline them! Another feature sorely lacking from Java, and implemented as a mere hint to the compiler in C++, Kotlin gives you full control over function inlining. Just mark the function as inline and you’re done.

This also enables non-local returns:

fun findPresident(persons: List<Person>): Person? { persons.forEach { if (person.isPresident) { return person // Returns from findPerson, not just from the lambda } } return null }

(More idiomatic would be persons.find { it.isPresident } . it is an implicit argument to any lambda unless you supply another.)

5. Non-nullable types by default

In most other languages, any reference type can be null. Not so in Kotlin. This will fail to compile:

val arthur: Person = null // Error! null is not a valid value for String.

And so will this:

var arthur = Person("Arthur", "Dent") ... arthur = null // Error! Inferred type of thingy is String.

That means you never need to worry about NullPointerException ever again. Mostly.

If you really want a nullable type, you have to add a ? to it:

var arthur: Person? = null // Works.

Then this will complain:

System.out.println(arthur.firstName) // Error! arthur might be null.

There are several constructs to deal with this:

System.out.println(arthur?.firstName) // null if arthur is null. if (arthur != null) { System.out.println(arthur.firstName) // Compiler sees that you checked. } val anonymous = Person("anonymous") System.out.println( (arthur :? anonymous).firstName) // Elvis operator: returns second argument if first is null. System.out.println(arthur!!.firstName) // Forcibly cast to non-nullable (last resort).

You’d think you’d need such features a lot, but in practice I find that most of my references can be non-nullable anyway.

6. The apply function

In Java, it’s typical to create an object, then set some fields on it:

Person zaphod = new Person(); zaphod.setFirstName("Zaphod"); zaphod.setLastName("Beeblebrox"); zaphod.setHeadCount(2);

Tedious and repetitive. If you’re doing this often, you will probably end up changing the Person class, returning this from each setter, so you can chain them:

Person zaphod = new Person() .setFirstName("Zaphod") .setLastName("Beeblebrox") .setHeadCount(2);

But if you don’t control the code, you’re out of luck. Not so in Kotlin! Enter the apply function, which works as a method on any object:

val zaphod = Person().apply { firstName = "Zaphod" lastName = "Beeblebrox" headCount = 2 }

Within the apply block, this refers to the Person we just created, and we can call methods on it without further qualification. The apply function uses the same technique as the DSL in #3. Notice how setters from Java are automatically transformed into properties, to make this even more readable.

The nice thing is that the return value of apply is the object you called it on, so it’s really easy to use this in a larger expression without being forced to introduce needless auxiliary variables.

7. Everything is an expression

At first I was confused by the lack of the “ternary operator” (more appropriately called “conditional expression”):

val happiness = person == marvin ? Int.MIN_VALUE : person.happiness

Until I realised that if is an expression, so you can just write this as:

val happiness = if (person == marvin) Int.MIN_VALUE else person.happiness

That’s not such a big deal, but look at these examples:

// No more need to declare it outside the try block, then // assign inside! val file = try { FileInputStream(...) } catch (ex: IOException) { logger.log(ex) throw ex } // "when" is like "switch" but with a different name // (and no fallthrough). val name = when (key) { 'a' -> "Arthur" 't' -> "Trillian" 'z' -> "Zaphod" else -> "Nobody" }

Ruby has the same, and has already demonstrated that it can lead to more compact and more readable code.

8. Single-expression functions

For short functions (like getters, which fortunately Kotlin doesn’t have you write), you can benefit from this syntax:

fun fullName() = firstName + " " + lastName

This is great for adding simple extension methods as well, and lets you program in a more functional style wherever it’s useful.

9. Free functions and variables

In Java, everything has to be in a class. If you want a function that’s unrelated to any class, you still have to make it a static method of some class.

In Kotlin, you can just define functions at the top level of the file. The same goes for variables and objects. There’s no need for the Singleton pattern in Kotlin; you just write it as a free variable of anonymous type:

val mySingleton = object { ... }

10. Algebraic Data Types

Disguised as the humble sealed class (similar to Java’s final class ), Kotlin actually lets you define full-blown algebraic data types. A traditional example is the linked list:

sealed class List { class Nil : List() class Cons(val value: Int, val succ: List) : List() }

Here, sealed means that only nested classes are allowed to inherit from it. This gives other code the guarantee that a List is either a Nil or a Cons ; no other values are possible. And the compiler knows this:

fun sum(list: List): Int = when (list) { is List.Cons -> list.value + sum(list.succ) // Compile error! Forgot to handle the Nil case. }

In practice, these can be very useful for state machines, where some states carry around some data that makes no sense in other states. For example:

sealed class GameState { class SettingUp() : GameState() class Playing(val boardPosition: BoardPosition) : GameState() class GameOver(val winner: Player) : GameState() }

The only other statically typed languages I know that can do this are Haskell and Haxe (not counting unions in C).

And more…

Kotlin has all the nice things that you’re used to from other languages, but are missing from Java.

Named arguments:

Person(firstName = "Tricia", lastName = "McMillan")

Default arguments:

class Person(firstName: String, lastName: String, headCount: Int = 1)

Lambdas:

{ title -> title.toUpperCase().substring(42) }

Higher order functions:

people.filter { person -> person.headCount == 1 }

Many more patterns are listed on the Idioms page on the Kotlin site. It’s a good place to get a feel for what the language is like.

Overall, I think Kotlin is a great language: safe by default, but also very pragmatic and pleasant to work with. Among the JVM languages I’ve tried (Java, Scala, Clojure, Groovy, JRuby), Kotlin is definitely my favourite.