How to betray Java in favor of Kotlin in 2018 without extra pain

Konstantin Mikhailovskiy
13 min readMay 15, 2018

--

So here you are, fellow Android Software Engineer from 2018, who is developing a pretty fast-growing Android app, still writing your code in Java, but at the same time being under pressure of all this hype about Kotlin, have already reached the point when you are finally ready to pull yourself off this Java comfort zone and change your life as a developer forever by adding far more convenience, brevity, functionality and sugar to it, and your project’s code base. And, what’s also quite important, your management or/and customers are ready for these dramatic (but worthy!) changes as well!

This article will lead you through the first steps of Kotlin expansion throughout your project and through the most common pitfalls you might as well run into.

Probably, the most logical and predictable question you might ask yourself first, when it comes to these small steps of Java betrayal in favor of Kotlin, is «Where should I start from?».

Open your doors for Kotlin

While there’s plenty tutorials about Kotlin configuration in your project, let’s focus on some first potential stumbling blocks on your Java betrayal path.

If you use annotation-processing or annotation-processing based libraries i.e. DI-frameworks like Dagger or views binding libraries like Butterknife or DataBinding) in your project, there’s a probability that it won’t compile without kotlin-kapt plugin. In some cases the lack of it after migration to Kotlin may seem quite unobvious and, without meticulous googling, bring you up to one terrible and extremely unproductive day of figuring out what has gone wrong. Just apply this plugin by adding the line below:

apply plugin: 'kotlin-kapt'

By the way, it’s extremely important not to forget to replace all usages of the annotationProcessor configuration with kapt in your build.gradle file!

Find more information on Kapt in official documentation: https://kotlinlang.org/docs/reference/kapt.html.

Also don’t hesitate to add Kotlin Standard Library dependency to your module-level build.gradle. You can confidently expect that this one will become handy enough (especially when it comes to different kinds of manipulations on collections).

implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlinVer"

Now, you are almost ready for the new era!

Step Zero: Null-safety

Okay, one more super-important point representing dramatic difference between Java and Kotlin, before you start coding.

Kotlin’s type system provides powerful Null-safety mechanism, so don’t hesitate to explore at least the following article in official documentation about how it works, and how to deal with nullable types themselves, if you haven’t read it already.

The very first date with Kotlin: Data classes

It’s a quite common point you may find in different posts about moving to Kotlin, that you should start from the unit-tests. Eventhough it makes real sense, as unit-tests is the least agressive and risky way to invade your existing code base and not to run into unexpected interop-issues, you might not feel this catarsis brought by language’s neat syntax and wide range of facilities to write better and more clean code overall. While you are free to start writing all your further unit-tests in Kotlin (we will come back to this point and its pitfalls a little bit later), in parallel you can find yourself creating your POJOs with Kotlin as well. Probably, you’ve already heard of data classes?

Say we need to create the entity-class for a Movie with several fields:

data class Movie(val id: Long, val title: String, val director: String, val releaseDate: Date)

This would be enough! You already have equals(), hashCode() and toString() methods overriden under the hood! Moreover, you get:

— The immutable class, which implicitly derives from Any, unlike Object in Java, with immutable (but not exactly) properties and implicit public getters and setters for each. Here’s how the instantiation of this class looks:

Movie(42L, "Isle of Dogs", "Wes Anderson", Date())

— The copy() function, which provides you the ability to clone the instance of this class, which can become super handy if you wish to create the new immutable object based on the another one, but with other values of some properties (unless they are not marked as private). This immutability approach brings you closer to the functional style.

val clonedMovie = existingMovie.copy(id = 43L)

Warning #1: in Java you’ll need to define all properties values as copy method arguments.

Warning #2: Just in case: equals(), hashCode() , toString() and clone() methods are not available for the instances of plain (NOT data) classes.

— Default values support, which have a strong potential to replace Builder pattern througout your project if you use it.

data class Movie(val id: Long = 0L, val title: String = "", val director: String = "", val releaseDate: Date, val description: String? = null)...val movie = Movie(releaseDate = Date(), title = "The Darjeeling Limited")

But keep in mind, there’s no inheritance supported when it comes to data classes! Unless you deal with sealed classes, implicitly abstract, which you may find useful if you wish, for instance, to declare different states of data loading or any other cases where restricted class hierarchies might be relevant.

«What if I need my data class to implement Parcelable?» — you’d probably ask.

No need to write boilerplate code anymore, as since 1.1.4 release it’s enough to dispense with @Parcelize annotation.

@Parcelize
data class Movie(val id: Long, val title: String, val director: String, val releaseDate: Date)

Just don’t forget to take these steps in order to make this approach work as desired:

— Apply Android Extensions plugin:

apply plugin: 'kotlin-android-extensions'

— Set experimental flag to true. But note that as the features like Parcelize are marked as experimental, it points on the fact that their API has not become finalized and stable yet, and might even change and break your code base in future after further updates.

android {
...

androidExtensions {
experimental = true
}

}

This is it! Enjoy the freedom of Parcelable boilerplate stuff!

But let’s go further with data classes compatibility with several common and beloved by Android-community third-party libraries.

Data classes meet common third-party libraries

If you use Room Persistence Library, you are free to use data classes for Room-entities creation. If you have kapt set up in your project, they’ll work with Room-annotations just fine:

Nullable properties work perfect as well. However, you should keep in mind that Room does not support Kotlin’s default arguments. Just in case this possibility ever comes to you mind (as it happened to our project team once).

What about common libraries which allow you to map JSON data to Java POJOs, like e.g. GSON?

No need for extra converters and plugins. GSON will serialize/deserialize your objects in the same way as it works with Java. Here’s how our serialization-readyMovie class looks like:

Note: But in the same way with Room there’s no chance for default values support.

For Jackson’s compatibility with Kotlin you, however, need to add a special Jackson module for Kotlin support.

In order to make Moshi support Kotlin you need to add extra moshi-kotlin dependency:

implementation 'com.squareup.moshi:moshi-kotlin:1.x.y'

Befriending Kotlin with Java: Interoperability

Now let’s imagine you already feel okay with Kotlin’s data classes and are ready to move further by starting to write more complex Kotlin classes like activities, presenters, view models, interactors, repositories, etc.

Of course, there’s a chance you are going to get rid of Java compeltely in your code base, but before Java’s final retirement you should take a long path of preventing Java and Kotlin from conflicts which might ruin your life (even if for several hours).

As Kotlin was designed as JVM-language fully compatible with Java and vice-versa, you are free just to start either extending Java classes, implementing interfaces and apply Java annotations to your Kotlin classes, as well as referencing Kotlin code in Java.

But there are still several BUTs formed by ideological differences between two languages (you can read about some of the primary ones here!).

Going static

For example, there is no static keyword in Kotlin, just deal with it.

But don't despair as companion object comes to the rescue! It represents object declaration inside a class and can contain constants and methods inside which are reachable from Java syntactically in the same way as Java static methods. However, don’t forget to mark your methods as @JvmStatic, so the compiler will generate both a static method in the enclosing class of the object and an instance method in the object itself (by default, without this annotation you can access companion object’s method with the reference to Companion instance). Check this documentation page for more details about companion objects, singletons creation in Kotlin and object expressions.

But, before you enclose all your constants in companion objects throughout the whole project, take into account that companion objects are not as cheap, as you might think. I’d deeply recommend everyone switching to Kotlin to read this series of articles about Kotlin’s hidden costs:

Collections

Kotlin relies completely on Java standard library classes, extending them with additional (including extension and inline like listOf(), mapOf(), etc, ones) mostly, fancy and handy functions for more convenient use. Things go almost clear here. Almost. ;)

You just need to note that immutable variants of the JDK collection classes are also available in Kotlin, and your ettempts to modify the contents of such collection (e.g. remove or add its element) will result in UnsupportedOperationException.

Generics

There is a support for Generics in Kotlin as well, but there are some major differences in their concept between two languages, ignorance of which may sometimes lead you to some extraordinary confusions, and these differences are thoroughly described in documentation and multiple informative articles, so I won’t duplicate their contents and will deeply recommend the ones, which I found the most helpful in the past and which helped me to understand the concepts of variance: covariance, contravariance and invariance.

  1. https://kotlinlang.org/docs/reference/generics.html
  2. https://proandroiddev.com/understanding-generics-and-variance-in-kotlin-714c14564c47
  3. https://typealias.com/guides/illustrated-guide-covariance-contravariance/

Befriending Kotlin with your Unit-tests

If you cover your business-logic with unit-tests (shame on you, if you don’t :), there is a big chance you use Mockito for these needs.

However, things will go wrong if you start writing your unit-tests in Kotlin without performing some extra steps. Let’s start from the fact that Kotlin-classes are final by default, which corresponds to Effective Java, 3rd Edition, Item 19: Design and document for inheritance or else prohibit it. And, as you probably may know, you can’t simply mock final classes. In this situation you can either:

  1. Open your Kotlin-classes by using open modifier (which in most cases would be a bad decision from the design point of view);
  2. Or apply a small hack to Mockito by creating the new file inside test/resources/mockito-extensions folder (all other paths won’t pass) with the following name: org.mockito.plugins.MockMaker. It should contain a single line: mock-maker-inline. Obviously, this plugin will generate mocks for your final classes, and your Mockito-tests in Kotlin will finally work for you as intended.

Moreover, I would personally recommend to use Niek Haarman’s mockito-kotlin library, which provides several useful helper-functions able to make your tests look cleaner.

For instance, there is a reserved identifier when in Kotlin which conflicts with Mockito’s when() method. In order to avoid the conflict you need to enclose when method’s name in backticks ``. Which, actually, doesn’t look good. Besides the syntactic sugar for more aesthetic stubbing, this library provides whenever method as a replacement for the ugly `when`.

Kotlin and Android make the powercouple: Activities

Finally, let’s go through the interaction with Android SDK briefly and start with activities.

Kotlin Android Extensions

When it comes to activities, in most cases we all start with layout inflation in onCreate method. If you use annotation-based alternatives to binding the views throught the classicfindViewById-approach, or use this approach per se, consider looking at Kotlin Android Extensions (don’t confuse them with Android KTX :) plugin: there’s a chance you might fall in love with it from the first lines of code.

Shortly speaking, with this plugin you can refer to the views by their ids directly, as accessible synthetic properties are already generated for them.

Find out more on this plugin setup and other use cases here: https://kotlinlang.org/docs/tutorials/android-plugin.html

Pitfall alert: if you use the <include> tag in your layouts, make sure your include-block does not override the id of the parent of the included layout.

E.g. the structure like this may lead into crash:

In this case, the original id of the included TextView (randomTextView) is overriden and thus, can not be found. The best solution would be to try to remove the id of the <include>-block, if possible, in order to avoid this confusion.

Befriending Kotlin with DataBinding

If you use DataBinding in your project and have no time so far to replace all its usages with Kotlin Android Extensions synthetic properties at once, you might notice this bunch of annoying compile-time errors after switching to kapt.

Thus, you need to enable DataBinding support for Kotlin explicitly in your build.gradle by adding DataBinding compiler dependency:

kapt 'com.android.databinding:compiler:x.y.z'

Here x.y.z. represent your current Gradle version: they may be the same.

Implicitly nullable argument pitfall

While you still need to work with API written in Java (including Android SDK), you should always be careful when it comes to the arguments of the open public methods which can actually be null.

Say, you need to override onActivityResult method in your activity class.

Do you see anything suspicious in this snippet?

Sometimes there’s a chance you (once it happened to our development team) can accidently omit the ?-operator after Intent which marks the argument of this type as nullable. So, from the Kotlin point of view, data will not be null by any circumstances, but whether you mark Intent as nullable here or not, you won’t get any compiler errors or even warnings, as both signatures are acceptable. But still, non-null data receival is definitely not guaranteed, as in some cases you can’t control this by yourself, and when it turns to be null, this will lead to NPE. Nullability-operator addition though will trigger the compile-time error if you try to e.g. call the random method on data without proper handling: it will force you to do it.

Kotlin and Android make the powercouple: Lambdas and adapters sweetening

As you might have noticed in one of the code snippets above, with higher-order functions we receive far less boilerplate code when it comes to e.g. view’s click listener implementation. You may start mastering the art of writing beautiful lambdas (and, for instance, figuring out in which cases you can omit the brackets to make your code expressions look as sweet as possible) with this page on documentation. We will not focus on it in this article and review several useful ‘recipes’ with lambdas as a main ingredient, as well as pitfalls you may run into as a result of combining Kotlin higher-order functions with common libraries like RxJava.

Cooking and serving RecyclerView adapter & ViewHolder in the right way

Assuming we have already applied Kotlin Android Extensions plugin, let’s see how we can implement the adapter & viewholder patterns in Kotlin, when we have a RecyclerView representing a list of Movie items which are clickable and selection of each should be somehow handled.

You’ve probably noticed (Movie) -> Unit as adapter’s constructor last parameter. In this case Movie stands for the type of receiver and Unit for the function’s execution result, and thus, replaces the hypothetical ‘click listener’ interface you would need to define in the same case dealing with Java.

The another part which could attract your attention is the MovieHolder class itself. As you can see it implements LayoutContainer interface which can turn movie item’s viewholder class to an Android Extensions container and enable the view caching feature (you are free to customize caching strategy by yourself).

Note: Don’t forget to override containerView: View property in your ViewHolder’s constructor!

And, finally, you have definitely noticed with-statement which is an inline function (so try not to misuse this statement in the irrelevant cases, as the anonymous class is created under the hood during its execution) allows you to call multiple methods on an object instance.

Another one potential pitfall alert: note that if you use the return keyword inside a lambda, it returns from the function in which the lambda is called, not from the lambda itself.

Befriending Kotlin with RxJava(2): And then… Nothing!

Kotlin dramatically reduces all the verbosity of RxJava (if you haven’t been using e.g. Retrolambda or Jack), and fully compatible with it. However, our world is quite imperfect place and there are still some gotchas which can make you puzzled.

Spoiler: it’s all about curly braces!

Once I became a victim of the infamous andThen problem, described in this post in detail: https://android.jlelse.eu/kotlin-and-rx2-how-i-wasted-5-hours-because-of-wrong-brackets-581021717774. Passing lambda to this method’s parameter will lead into disappointing existential nothing, and the Completable result of this function won’t be executed. Actually, CompletableSource is expected as the parameter instead, and this one will work as expected. Actually, it’s not only about andThen: just be careful with the operators which accept ObservableSource, SingleSource or CompletableSource as an argument, and prefer them to lambdas (otherwise, you take a risk of spending several hours feeling lost and frustrated :).

By the time of this article publication, this issue is still under discussion.

Conclusion

This article covers only the tiny amount of the common pitfalls and questions you might run into during the migration of your project from Java to Kotlin. In most cases the primary point which can lead us to these issues is the spaces in our knowledge of Kotlin and practical experience of coding in it, but the more improvised puzzlers you try to solve in your developer’s life, the better developer you become. From my point of view, even your decision to finally leave the Java comfort zone in favour of Kotlin will result in your evolution as Android developer, so if you have this possibility, start switching to it, and despite all the potential pain, you’ll fall in love with your job again, instantly.

If you know any other Kotlin lifehacks applicable to Android development, or mysterious pitfalls you’ve struggled with, please, share them with the others in comments. :)

Useful links and sources

  1. Take a look at Android KTX. It can really simplify your life as a developer and spare your time and lines of boilerplate code on the operations like Bundle object population, working with Shared Preferences, bitmaps, text spans, animations, and so much more. Beside the official documentation, I’d recommend you to read this article as well: https://medium.com/exploring-android/exploring-ktx-for-android-13a369795b51.
  2. Don’t forget to use Kotlin Styleguide for Android.
  3. Kotlin in Action book! You’ll never regret spending your time on it.

Thanks for reading!

--

--

Konstantin Mikhailovskiy

Android Software Engineer @ Genesis Tech (BetterMe), amateur photographer, geek, backpacker.