In this blog post, I want to illustrate why we are using Kotlin instead of Java as a programming language for developing Android applications and give a high-level overview of the key benefits we gained by using it. I will also talk about how Kotlin emerged in the Android community and show how the future of mobile development could look like with Kotlin Multiplatform projects.
Kotlin Historic Development
In 2010, JetBrains started to work on Kotlin, a new programming language for the development of their IDEs, because they had some problems with their large-scale Java codebases. Kotlin compiles down to Java Bytecode and is therefore fully interoperable with Java. This was very important for JetBrains, as they did not want to throw all their existing Java code away. The interoperability allows Kotlin to be used for every application that runs on the JVM, like Android applications for instance.
The first time Kotlin received attention from the Android community was back in 2015, when Jake Wharton, a famous Android developer, published a document called “Using Project Kotlin for Android Development”. Afterwards, a couple of cutting-edge companies started to use Kotlin for the development of their production applications, but a lot of companies were not sure whether the switch to Kotlin is too risky since it was uncertain how Google would handle this new language after all.
At Google I/O 2017 however, a lot of developers were positively surprised when it was announced that Kotlin will be a first-class supported programming language for Android. More companies got more confident and started to adopt Kotlin. Currently (At the beginning of 2019), about 46% of professional Android Developers use Kotlin according to a survey from developer.android.com (information from the Android Developer Summit 2018 keynote). According to Github’s yearly statistics of 2018, Kotlin is the #1 fastest growing language in terms of contributors. Stack Overflows Developer Survey of 2018 places Kotlin as #2 of the most loved programming languages.
Advantages over Java
Kotlin is very concise and rough estimations are, that you save about 40% of lines of code compared to Java. The syntax in Kotlin expresses the intent of the code much clearer and does not get obscure with as much boilerplate as we see in Java. This is important because the code is read far more often than it is written and if developers have less they need to read and the intent of the code is easier to comprehend, they will understand the code faster.
The following language features make this high level of conciseness possible:
In Java, classes have fields and associated getters and setters have to be written. In Kotlin, classes have properties instead of fields. You don’t need to explicitly write getters and setters for them because the compiler is doing that work for you when it generates the Java Bytecode, which then contains this getters and setters. You won’t lose the benefits of encapsulation because you later can add custom getters or setters if you need them without breaking the API of the class.
In Java, if you want to check the equality of value objects or store them into collections, you have to override equals() and hashCode(), which is time-consuming, results in some boilerplate and is easy to get wrong. All this work can be done in Kotlin by the compiler. All you have to do is prefix your class with the data keyword and the two methods are generated in the byte code of that class.
In Kolin, properties can be declared directly in the primary constructor. So no repetitive assignment of constructor arguments to class fields is needed.
Very often, the Kotlin compiler can infer the type of an object, so most of the time you don’t need to state it explicitly.
Function signatures in Kotlin can include default arguments, which are used if the caller of that function does not provide an argument for that parameter. So no function overloading is necessary.
Top Level Functions and extension functions
In Kotlin, functions can be defined outside of any class. Those types of functions are called top-level functions. No Utils classes with static functions are therefore necessary. You can also extend types for which you do not have the possibility to modify with extension functions.
Powerful type system
The type system of Kotlin is much more powerful, mainly because of the following features of the language:
Nullability is handled directly in the type system. You declare objects as nullable (can be null) or non-nullable. If you want to access a property or a method of a nullable object, the compiler forces you to do a null check beforehand. Otherwise, the code will not compile. NullPointerExceptions at runtime, which were the most frequent cause of crashes of our application, are therefore a thing of the past.
No primitive and reference types
In Java you have to be careful, whether to use a primitive type (e.g. int) or a reference type (e.g. java.lang.Integer). Making a wrong decision can lead to bad performance because of an unnecessary big memory overhead or unintended autoboxing. In Kotlin there is only one variant (Int) for an integer type. The compiler makes the decision whether to use the primitive type or the reference type in the bytecode.
Kotlin has several language features that support developers with writing functional programs. Functions are treated as first-class citizens in Kotlin. You can declare function types just like any other types, pass functions to other functions and also make functions return other functions.
Streams in Java 8 revolutionized the way developers work with collections. Unfortunately, not all Java 8 features are available for Android development yet and some are only available if your application targets only new versions of Android. That’s also the case for streams, which are only available if your application targets API level 24+ (Android 8.0 Oreo and higher).
Kotlin, however, provides a lot of functional APIs in its standard library, which let you work with collections in a similar manner as with streams in Java 8. So all those nice functions like map() and filter() are available in Kotlin as well, making your collection processing more concise and readable.
Asynchronous programming was always hard in Android. In the early days, we mainly used AsyncTasks (a helper class around threads and handlers) for that, but it was really tricky and cumbersome to work with and required a lot of boilerplate. Then, RxJava became more and more popular and lots of apps used it merely to simplify asynchronous code. However, RxJava is a really comprehensive library with a steep learning curve and its main strength lies in processing streams of data. We decided that using it just for simplifying threading might be an overkill and switched to Kotlin Coroutines, which are designed specifically for that.
The first advantage of coroutines is that the syntax is really easy to understand and already familiar to developers, because asynchronous code is written in the same sequential manner as synchronous code, with the use of suspend functions instead of e.g. callbacks.
The other advantage is that coroutines are much more lightweight than threads, which consume a considerable amount of resources and switching between them is a fairly expensive operation. Because of the high memory footprint, you run out of memory pretty fast by creating lots of threads. On the other hand, it is possible to create a lot of coroutines before running out of memory.
The Future? — Kotlin Multiplatform
For mobile applications, this means that it would be possible to share a lot of code between Android and iOS. The philosophy behind Kotlin Multiplatform projects compared to other cross-platform technologies is that only things like business logic, networking, and persistence are shared. User interfaces and platform-specific features must still be developed in the conventional “native” way. The shared Kotlin code is accessed via another gradle module(Android) or from a generated framework (iOS). This means that it is relatively risk-free to try and switch back if the Kotlin Multiplatform concept did not work out as desired.
Summary: No regrets
Over a year passed now after we made the switch to Kotlin and we have to say that we don’t regret it. The high level of conciseness makes our files and classes small and easy to understand. Nullability in the type system helped us to increase the percentage of crash-free users. Functional constructs help us to use functions as building blocks of our application and coroutines made working with asynchronous code so much easier. Last but not least, writing Kotlin code with its modern approach is much more fun than writing Java code, which feels really laborious once you worked with Kotlin for some time.