Table of Contents
Why Kotlin language?
If today I was asked what is one of the characteristics that distinguishes the development of Android applications from the rest of the fields, I would not hesitate to answer that the possibility of executing the same application on devices with a different hardware in a native way is one of them; but … how is this possible? And today I would like to start my series of Kotlin language articles, explaining the language and the benefits of it.
At this point no one is surprised to see the same web application running on any device and on any platform (Android, iOS, Windows, MacOS …), we all know that these applications are slower and more unstable than any native application; but in exchange we only have to develop one application for all platforms. A similar problem would arise when talking about the number of different devices on which Android works right now; and I say would if it wasn’t because of Java. The power of Java and the fact that it is used in billions of devices today, it’s ability to work on any device regardless of its hardware and software, as long as it has an interpreter of the pseudo-compiled code generated by the Java compiler (The official Java interpreter is the Java Virtual Machine, although on Android Dalvik was used in the first versions and ART today).
Does this mean that Java is the solution to all evils? Unfortunately, nothing is further from the truth … Although Java solves the problem of interoperability between devices, it opens a new range of headaches that we would like to be able to remove from the equation, some of them *:
* Note: Many of these problems, although resolved in Java 8 and 9, are not available in the Android SDK below API 24, which makes them practically unusable)
- There is no native support for optionals (although we do have it for immutable collections). Although there is the Optional <T> class, its use implies the generation of a large amount of boilerplate code, which we could save if the support for the options was built within the language itself and not as an extension of it.
- There is no native support for functional programming: In Java there is the Stream Api (Once again it only has support in Android starting from API 24), but its integration in the language is similar to the one of Optional, it exists poorly in the objects associated with primitive types (IntStream, DoubleStream …), and through a Stream class <T> for all other objects.
- Support for anonymous functions (Lambdas). Although Java 8 incorporates support for Lambda functions, these are not first-class citizens; this means that, although we can use lambdas to implement anonymously interfaces with a single method, Java does not support passing functions as arguments to a method.
In addition, the lack of type inference makes the statement of Lambdas quite uncomfortable, especially in the attempt to simulate functions such as composition of functions or currying; lack of support for them in the language. - Type nullability: Although this can be included within the section referring to the optional, the dimension of this problem deserves a special mention. How many Java programmers didn’t fill their code with if (foo! = Null) to try to fight the dreaded NullPointerException? (Actually, it’s creator apologised for what he calls a “billion-dollar mistake”) And how many of those check ups are more than patches to avoid a crash in our application?
- Binding of manual views: Although this problem is specific to Android as a Platform, and not Java as a language, it is also worth pointing out the amount of boilerplate code needed to obtain a reference to an Android view. Although we have managed to eliminate the hated findViewById (int id) thanks to dataBinding, we still have to store a reference to that binding.
- It is more general, but not less important, Java is a very verbose language, it requires writing a large amount of code for any operation, as well as generating a large number of files (one per class). The first problem can lead us to a code more expensive to maintain and more prone to errors. The second is a problem of class proliferation.
Why Kotlin language breaks with all this?
It is for all these reasons that, today, Java is considered as a language that, at least in Android development, does not evolve at the speed that the industry does.
As time passes, the need to have a language with real and native support for everything mentioned above becomes more imperative, as well as maintaining the main feature of Android exposed at the beginning of this article, its ability, writing and compiling a single application, make it work on any device and version of it.
In this direction many possibilities have been explored, some of them being the use of Swift or Scala, although none has been very promising.
All this changed with the appearance of Kotlin language. Kotlin is a language designed and developed by Jetbrains, focused on being a modern language, in constant evolution and, above all, that can be executed on the JVM. This makes it a perfect candidate to be used on Android.
To begin to demonstrate it, we can list down all the cons we face with Java and how Kotlin language acts in front of them:
- Optionals. They’re built in inside Kotlin, all you have to do is declare the type of a variable ending in a question mark ? so it becomes an optional type. Kotlin language also provides the possibility of safely unwrapping those optionals listener?.onSuccess() without checking if there’s a value for this optional, and also provides the Elvis Operator.
- Functional programming: In Kotlin we find native support to work with collections and datasets like Streams. We can directly call .flatMap {} in a collection, as well as .filter {}, .map {}, and many more. The inference of types makes the use of Lambdas especially manageable.
- Lambdas and high order functions: The previous point is completed with the fact that in Kotlin language, the functions are first class citizens. We can define functions that receive other functions as parameters. An example of this is the definition of the map function itself:
inline fun <T, R> Iterable.map(transform: (T) -> R): List (source)
Although at first sight this code may seem a bit chaotic, the part that interests us is
transform: (T) -> R
This means, the map function has a parameter called transform, which is itself a function that has an input parameter of type T and returns an object of type R.
Thanks to this native support for lambdas, in Kotlin language we can use the map function such that:
collection.map { item -> aTransformation(item) }
This code snippet will return a collection of elements of the type returned by aTransformation.
- Type nullability: In Kotlin language, since there is an integrated support in the language for optionals, we should have the minimum possible number of nullables in our code. But even so, if it exists, Kotlin offers us tools to deal with them easier than in Java. For example we have the operator safe call (?) to avoid NullPointerException when accessing an optional, or with the operator safe cast to protect us in case of wanting to perform a casting. The compiler of Kotlin, in addition, forces to control the types that could have null value, and even introduces runtime checks in case of compatibility with Java code.
- Binding of views: This being a specific Android problem, Jetbrains offers us Kotlin Android Extensions; an official support library to simplify this problem (and some other) through a gradle plugin.
- Verbosity of language:
Java
public interface Listener {
void success(int result);
void error(Exception ex);
}
public void someMethod(Listener listener) {
int rand = new Random().nextInt();
if(listener != null) {
if (rand <= 0) {
listener.onError(new ValueNotSupportedException());
}
else {
listener.success(rand);
}
}
public void fun(Type1 param1) {
param1.someMethod(new Listener() {
@Override
public void success(int result) {
println(“Success” + result);
}
@Override
public void error(Exception ex) {
ex.printStackTrace();
}
}
}
Kotlin
fun someMethod(success: (Int) -> Unit, error: (Exception) -> Unit) {
val rand = Random().nextInt()
if (rand <= 0) {
error(ValueNotSupportedException())
else {
success(rand)
}
}
}
fun (param1: Type1) {
param1.someMethod( {
println(“Success” + it);
}, {
it.printStackTrace();
})
}
Or even, using expressions:
fun someMethod(success: (Int) -> Unit, error: (Exception) -> Unit) {
val rand = Random().nextInt()
if (rand <= 0) error(ValueNotSupportedException()) else success(rand)
}
fun (param1: Type1) {
param1.someMethod( {
println(“Success” + it);
}, {
it.printStackTrace();
})
}
It is up to the reader to decide which of the two snippets is easier to write and interpret.
All the discussed above, along with another large number of features that did not fit in this article or that were not the ones that really matter to us shows us that Kotlin language seems the most promising bet for the next few years in the world of mobile development. In my next articles, we will study more in detail what benefits we get by using Kotlin in Android development and its impact on the industry.
And if you are interested in mobile development, I highly recommend you to subscribe to our monthly newsletter by clicking here.
If you found this article about Kotlin language interesting, you might like…
iOS Objective-C app: sucessful case study
Mobile app development trends of the year
Banco Falabella wearable case study
Viper architecture advantages for iOS apps
Author
-
Android developer with over 8 years of experience on the platform. Passionate about software architecture, best practices, design patterns & teamwork.
View all posts
5 Comments
Moritz
I implemented your example in proper modern Java and it is no longer then your Kotlin code. Your shortest Kotlin sample has 7 meaningful lines of code, the proper Java implementation is no longer.
Your Java code is only longer and more complicated than your Kotlin code because it is not the best solution that Java has to offer. Its hard to see an advantage of Kotlin when you compare proper Kotlin code with proper Java code:
void someMethod(Consumer success, Consumer error){
int rand = new Random().nextInt();
if(rand System.out.print(“Success ” + i),
e -> e.printStackTrace()
);
}
Moritz
Somehow my code got cut off in the end, here the full sample:
void someMethod(Consumer success, Consumer error){
int rand = new Random().nextInt();
if(rand System.out.print(“Success ” + i),
e -> e.printStackTrace()
);
}
Moritz
And cut off again… last try; here the second (and missing) half:
void fun(Main main){
main.someMethod(
i -> System.out.print(“Success ” + i),
e -> e.printStackTrace()
);
}
Diego Ojeda
Hi Moritz,
You’re right when you say that modern Java provides mechanisms to implement the sample with the same numbers of lines of code, the point here is that many of those tools (such as lambdas, streams or the Consumer class that you’re using here) are not available on Android in a way that makes them usable.
Java 8 is not fully supported on Android at the moment, and neither are lambdas. We can use Retrolambda for that, but it involves setting up a gradle plugin and add some more steps on the build phase; while lambdas are built in on Kotlin.
Also there are many APIs that are not available for an API level below 24 (and Consumer class is one of them), as I mention in the article this makes those classes virtually unusable, as nobody develops nowadays with a minimum API level of 24.
Regards.
Tim
Kotlin is overrated.