Kotlin for grumpy Java developers

Sha Sha Chu | Android Platform tech lead, Core Experience

So you’re an Android developer. You’ve been writing Java code for years and your code is clean, testable and well-architected. Then Google announces official support for Kotlin at Google I/O and rocks the developer world. Suddenly your team is all about Kotlin and your lunchtime conversations are full of words like “lambdas with receivers,” “nullability” and “extension functions.” You stay heads down and continue writing your great Java code. After all there’s nothing Kotlin can do that Java can’t, right? But day after day, more .kt files appear in your codebase until it’s clear that you can’t stay in your Java bubble forever–you’re going to have to learn Kotlin. Fear not! For I was once like you. I can guide you, Grumpy Java Developer, through your first lines of Kotlin.

Learn the basics

The good news is there are only a few basics you’re required to know before you can jump into Kotlin. The bad news is there are a few basics you’re required to learn before you can jump in. Fortunately, since Kotlin has been steadily gaining popularity, there are a number of great resources for learning the basic syntax of Kotlin. When you first start, try to focus on the very basic concepts such as the difference between val and var, Type and Type? and the syntactical differences between Java and Kotlin; including the type coming after the variable and function declarations.

Kotlin code can look a lot like Java…and that’s okay

A common trap new Kotlin developers fall into is jumping straight into completely idiomatic code and getting overwhelmed. While one of its benefits is terseness, there’s no need to pare down your Kotlin files to the minimum number of lines, especially if you’re new to the language.

Consider this completely valid block of Kotlin:

fun logVideoQuartile(player: VideoPlayer?) {
if (player == null) {
return
}
    // playPercentage is a property on VideoPlayer
val percentComplete: Int = player.playPercentage
val quartile: Int
val percentQuartile: Float
    if (percentComplete >= 0 && percentComplete < 25) {
quartile = 0
percentQuartile = 0f
} else if (percentComplete >= 25 && percentComplete < 50) {
quartile = 1
percentQuartile = .25f
} else if (percentComplete >= 50 && percentComplete < 75) {
quartile = 2
percentQuartile = .50f
} else if (percentComplete >= 75 && percentComplete < 95) {
quartile = 3
percentQuartile = .75f
} else if (percentComplete >= 95 && percentComplete < 97) {
quartile = 3
percentQuartile = .95f
} else if (percentComplete >= 97 && percentComplete < 100) {
quartile = 3
percentQuartile =.97f
} else if (percentComplete == 100) {
quartile = 4
percentQuartile = 1f
} else {
quartile = -1
percentQuartile = -1f
}
    println(“Quartile is $quartile and percent quartile is $percentQuartile”)
}

You’ll notice it looks a lot like Java code–and that’s okay! Even if you left this code as is, Kotlin has already helped you in two ways. First, since player is a nullable type, you’re forced to add a null check. Second, since quartile and percentQuartile are declared as vals, you’re forced to add the else case which will catch erroneous input values.

Lean on your IDE

Once you’re comfortable with the basics of Kotlin, you can start delving into the more interesting language features. Because Kotlin was written by the authors of IntelliJ (on which Android Studio is based), there’s excellent IDE support for it. Considering again the video quartile code, you’ll notice the IDE added squiggly lines under each if statement:

If you hover your mouse over the statement, you’ll see the IDE suggesting that you convert these checks into range checks.

You don’t know what a range check is, but use option+Enter to try out the suggestion:

And just like that, you’ve learned a new language feature. Your checks should now look like this:

if (percentComplete in 0..24) {
quartile = 0
percentQuartile = 0f
} else if (percentComplete in 25..49) {
quartile = 1
percentQuartile = .25f
} else if (percentComplete in 50..74) {
quartile = 2
percentQuartile = .50f
} else if (percentComplete in 75..95) {
quartile = 3
percentQuartile = .75f
} else if (percentComplete in 95..96) {
quartile = 3
percentQuartile = .95f
} else if (percentComplete in 97..99) {
quartile = 3
percentQuartile = .97f
} else if (percentComplete == 100) {
quartile = 4
percentQuartile = 1f
} else {
quartile = -1
percentQuartile = -1f
}

Note: The functionality is exactly the same, but the range notation is arguably easier to read and closer to how you, the developer, think about the code.

Going further, while the IDE doesn’t explicitly call this out, you might notice that if you press option-Enter on the if statement, you’ll see another suggestion to convert the if statement to a when. By accepting this suggestion, the code now becomes:

when (percentComplete) {
in 0..24 -> {
quartile = 0
percentQuartile = 0f
}
in 25..49 -> {
quartile = 1
percentQuartile = .25f
}
in 50..74 -> {
quartile = 2
percentQuartile = .50f
}
in 75..94 -> {
quartile = 3
percentQuartile = .75f
}
in 95..96 -> {
quartile = 3
percentQuartile = .95f
}
in 97..99 -> {
quartile = 3
percentQuartile = .97f
}
100 -> {
quartile = 4
percentQuartile = 1f
}
else -> {
quartile = -1
percentQuartile = -1f
}
}

Again, we haven’t changed the functionality, but the block becomes easier to read.

Experimenting with IDE suggestions can be a great way to explore different Kotlin language features, all in the context of code you already know. Additionally, using ‘Navigate to Declaration’ (command-B by default) gives you hints about how various language features are implemented. For example, jumping to the definition of in shows you it actually calls IntRange.contains.

Finally, IntelliJ/Android Studio supports automatically converting Java code to Kotlin, both with whole files and by copy-pasting Java code snippets into a Kotlin file. This can be helpful when you can’t quite figure out Kotlin syntax. When I first started, I didn’t know the syntax for adding a listener to an Android Animation (as an anonymous class), so I wrote the code in Java and copy-pasted it in. (For those wondering, I was missing the object keyword.)

Expanding your Kotlin vocabulary

Once you’ve had a week or so writing Kotlin code, it’s time to start exploring some of the more interesting language features. Let’s revisit the quartile logging code and see if we can apply some new tricks.

I’m not a big fan of early returns in functions, because it can cause bugs when you move code around. For the initial null check on player, instead of doing an early return, there’s a Kotlin function we can use:

fun logVideoQuartile(player: VideoPlayer?) {
player?.apply {
val quartile: Int
val percentQuartile: Float
when (playPercentage) {
in 0..24 -> {
quartile = 0
percentQuartile = 0f
}
...
}
}
}

The code inside the apply block will only run if player is not null (note the ?. operator). You could achieve the same effect with an if (player != null) block in Java, but the apply operator also sets the this context of the block to the player instance, so we can refer to playPercentage directly without having to reference player.

Moving along, another suggestion that the IDE makes is to “Lift assignment out of when.” If you apply it, it does something interesting but not quite ideal:

val quartile: Int
val percentQuartile: Float
percentQuartile = when (playPercentage) {
in 0..24 -> {
quartile = 0
0f
}
in 25..49 -> {
quartile = 1
.25f
}
...
}

You’ll notice that instead of repeating the percentQuartile assignment in each in block, it does a single assignment outside the when block. However, we’ll have the repeating quartile assignment. Wouldn’t it be nice if we could assign these variables together? In Java you’d have to create a new class, but in Kotlin, there’s a standard library class called Pair we can put to use here:

player?.apply {
val(quartile, percentQuartile) = when (playPercentage) {
in 0..24 -> {
Pair(0, 0f)
}
in 25..49 -> {
Pair(1, .25f)
}
in 50..74 -> {
Pair(2, .50f)
}
in 75..94 -> {
Pair(3, .75f)
}
in 95..96 -> {
Pair(3, .95f)
}
in 97..99 -> {
Pair(3, .97f)
}
100 -> {
Pair(4, 1f)
}
else -> {
Pair(-1, -1f)
}
}
println(“Quartile is $quartile and percent quartile is $percentQuartile”)
}

(The assignment might look strange to Java developers because each in block is returning a Pair instance, but the left side of the assignment is two separate variables, quartile and percentQuartile. This is called destructured declaration and is simply a convenient syntax for declaring and assigning multiple variables in a single statement.)

We’re getting closer! Another IDE suggestion for each in block is to remove the braces from the when entry. Our code now becomes:

fun logVideoQuartile(player: VideoPlayer?) {
player?.apply {
val(quartile, percentQuartile) = when (playPercentage) {
in 0..24 -> Pair(0, 0f)
in 25..49 -> Pair(1, .25f)
in 50..74 -> Pair(2, .50f)
in 75..94 -> Pair(3, .75f)
in 95..96 -> Pair(3, .95f)
in 97..99 -> Pair(3, .97f)
100 -> Pair(4, 1f)
else -> Pair(-1, -1f)
}
println(“Quartile is $quartile and percent quartile is $percentQuartile”)
}
}

The function is now compact but still readable. A subtle benefit of this structure is the block becomes easier and intuitive to update should our logic change in the future. (Note: some of you might point out that we can shorten the code further by converting the Pair statements to use the to function: in 0..24 -> 0 to 0. It’s simply a personal preference of mine to leave the code as written, because the to function reads more like a range than a mapping/tuple to me. Additionally, it may be clearer to use the until function for the ranges, which is similar to the .. range operator, but it doesn’t include the ending value. This is just one example of multiple ways to write the same line of code in Kotlin.)

Kotlin won’t make bad code good

At the end of the day, Kotlin is just another tool. It’s not going to magically bring your app’s crash rate to zero, and despite what you might have heard, it’s still possible to get NullPointerExceptions in Kotlin code, especially at the boundaries between Java and Kotlin. I resisted learning Kotlin for several months when it was first introduced into our codebase. I argued that it couldn’t do anything that Java couldn’t. However once I started working on a feature that was written exclusively in Kotlin, it forced the issue. I took the approach outlined here, and to my surprise, I felt “conversational” in Kotlin in about a week, and it only took me another two weeks to feel like I had learned a number of intermediate to advanced language features.

Kotlin is not a silver bullet, but it does have many powerful features that directly address some of the shortcomings of Java. This helps move some of the cognitive load off you onto the compiler, so you can focus on simply making good products. And maybe, just maybe, that will make you a little less grumpy.

Acknowledgements: All the Android developers at Pinterest for making every day at work fun and interesting. In particular, Matt Beattie, Dom Bhuphaibool, Ryan Cooke, Vy Phan, and Zach Westlake for their help in writing and editing this blog post, and Christina Lee for championing Kotlin at Pinterest and finally making this grumpy developer learn the language.