Lessons from converting an app to 100% Kotlin

AJ Alt
AJ Alt
Aug 11, 2016 · 7 min read

This is part one in a series of posts about Kotlin. Click here for part 2, where I discuss the design of the Kotlin language.

I’ve been following the development of Kotlin for a while. Kotlin is a relatively new language that primarily targets the JVM, and is interoperable with Java. With the release of Kotlin version 1.0.2, which brought incremental compilation and a large reduction in the number of methods in its standard library, I was eager to start using it in production.

I’m the lead engineer on App Lock at Keepsafe, which, like most Android apps, was written in Java. There are quite a few places where Java falls short of modern languages, especially the version of Java 7 that Android supports. To reduce the pain, it is common to use libraries such as Retrolambda for a backport of lambdas and try-with-resources, Guava for immutable collections and utility functions, ButterKnife for view binding, or ReactiveX for functional programming. All of those libraries come with drawbacks, though. Retrolambda frequently causes incremental builds to fail, and every library you depend on adds methods to your APK.

Even with those libraries, Java code is verbose. Your code has to go through a lot of ceremony that the designers of Java thought was a good idea in the 90s, but is clearly unnecessary today. Kotlin provides a well thought-out syntax and extensive standard library that removes many of the pain points that exist in Java. So over the course of a few days, I converted the entire App Lock codebase into Kotlin. Here are my thoughts on the process.

Kotlin vs Java in App Lock

Image for post
Image for post
Method count in App Lock after converting to Kotlin. From the dexcount gradle plugin.

One concern that people will raise about converting to Kotlin is the number of methods added by its standard library. Thanks to massive libraries like the support library and GMS, many apps are in danger of bumping up against the Dex method limit. I used the dexcount gradle plugin to break down the method count before and after the rewrite. The end result is that the total method count after proguard decreased by 10% from 5491 to 4987 (not counting GMS or appcompat). The code count decreased by 30% from 12,371 lines of Java, to 8,564 lines of Kotlin.

Converting the app from Java to Kotlin reduced the total method count by 10%, and the total lines of code by 30%

The method count decrease is a result of both Kotlin being a more concise language, as well as the fact that many of the quality of life libraries that were previously used in Java are no longer necessary.

Retrolambda

For example, the extremely useful standard library function apply:

public inline fun <T> T.apply(block: T.() -> Unit): T { 
block(); return this
}

Which is called like this:

myObject.apply { /* modify myObject */ }

Even though you’re defining a lambda function at the call site, no anonymous class is generated, so no extra methods are added, and no allocation happens due to that call. In fact, the apply function itself, like most inline functions in the Kotlin standard library, doesn’t cause a method to be added in the compiled code.

Guava

// Guava
ComparisonChain.start()
.compareTrueFirst(lhs.isActive(), rhs.isActive())
.compare(lhs.lastName(), rhs.lastName())
.compare(lhs.firstName(), rhs.lastName())
.result();
//Kotlin
compareValuesBy(lhs, rhs,
{it.active}, {it.lastName}, {it.firstName})

The null safety of Guava’s Optional class is built in to Kotlin.

// Guava
return Optional.of(value).or(defaultValue);
// Kotlin
return value ?: defaultValue

Guava’s lazy fields and Preconditions classes are covered by the Kotlin standard library.

// Guava
private Supplier<String> lazyField = Suppliers.memoize(
() -> "value");
public String getField() {
return lazyField.get();
}
// Kotlin
val field by lazy { "value" }
// Guava
Preconditions.checkNotNull(value, "error %s", arg);
// Kotlin
checkNotNull(value) {"$arg"}

Nearly all of Guava’s collections classes exist in Kotlin. Even with all of that functionality, the entire Kotlin standard library is still smaller than Guava alone.

ButterKnife

RxJava

Observable.from(collection)
.filter(it -> it.isActive())
.map(it -> it.size())
.reduce((it, sum) -> it + sum)
.toBlocking().single();

Can be replaced with this in Kotlin:

collection.filter { it.isActive() }
.map { it.size() }
.reduce { it, sum -> it + sum }

Getting started with Kotlin

The best part about Kotlin is that it can be called from Java, and Java can be called from Kotlin. So you don’t need to convert your entire codebase at once. I would suggest you start with a single file that you rewrite from scratch. IntelliJ has an automated Java-to-Kotlin converter, but it frequently produces incorrect code, so it’s better to start from scratch until you have a better handle on the language.

Once you have the basics down, I recommend picking some existing Java code that has few dependencies, and converting it to Kotlin. UI code like an Activity of Fragment on Android is a good place. Picking a class with no dependencies allows you to focus on just the code you’re working on without worrying about how any interfaces are changing. Keep the Kotlin reference open so that you can quickly answer questions about syntax or the standard library that come up while you’re working. You could also choose to start new Kotlin code from scratch instead of converting from Java, but I find that it’s easier to pick up some of the less obvious syntax from converted code than trying to figure it out from a blank slate. The automated conversion does a really good job in most cases, and the places where it fail are usually easy to fix.

Something to keep in mind while you’re learning Kotlin is to avoid getting overwhelmed. If you don’t already use a design pattern like MVP or MVVM, don’t worry about try to learn it at the same time. Don’t worry about finding every Kotlin library available. Just focus on taking what you know about Java and translating that knowledge to Kotlin. If you still have pain points, then you can add more libraries or design patterns.

Should you convert your entire codebase at once?

For large codebases, it can be too much work to convert everything in one release. In Keepsafe’s main app, about 15% of the code is Kotlin as of this writing. In that app, if we have to make significant changes to a Java class, we’ll usually convert the class to Kotlin while we’re working on it. This allows us to steadily improve the codebase without slowing down our work on new features.

However, if your project is small enough that you can convert to 100% Kotlin, it’s something worth considering. When you don’t have to maintain Java compatibility, you can simplify your internal APIs and remove a lot of the libraries I talked about earlier. You can convert static utility classes into extension functions, and take advantage of the stronger type inference in Kotlin.

Final thoughts


This is the first post in a series on Kotlin. Follow Keepsafe Engineering to get notified about future posts. Interested in writing Kotlin code yourself? Have a look at Keepsafe’s Kotlin job opening.

Keepsafe Engineering

Engineering problems at Keepsafe.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store