I tried Kotlin on Android and I’m never coming back to Java
Krzysztof Wojciechowski
I don’t remember when exactly I heard about Kotlin for the first time. Some people that I’ve talked about it were excited, others had more moderate opinions.
Java had been around for a while, being the primary language for Android since its first days, also proving itself useful in many other applications. But since Google announced first-class support for Kotlin, as it gained more and more popularity, it became clear that learning Kotlin will be a necessity for Android developers, sooner or later.
I dedicated some of my private learning time to it and really liked many of its features. So when itCraft decided to write new projects in Kotlin I got really excited, now I could put my freshly learned skills to test in a commercial development environment.
As you may have guessed by the title, my passage was smooth and enjoyable and I found writing in Koltin very enjoyable, compared to Java. Therefore I present to you a list of features that convinced me to sail further towards Kotlin. (because it’s also an island named Kotlin, got it? :P)
Null safety
It seems that null-safety is the first Kotlin feature that comes to mind of the programmer with Java background. Null reference is a feature present in most of the programming languages. It may seem useful, but it turned out to be problematic, introducing many possibilities for a program to malfunction and crash.
Even its inventor called introducing it, a billion dollar mistake, as it often requires long and precious man-hours to debug null-related problems. Kotlin addresses this problem directly, forcing the programmer to explicitly declare a property as nullable.
var foo = “string” //OK
var bar = null //compilation error
var baz: String? = null //OK
It is only possible to assign null to the property of a nullable type, annotated with ?. To execute statements containing such types, the compiler forces us to use safe call.
bar.callFunc() //compilation error, bar may be null
bar?.callFunc() //OK
Instead of crashing, the safe call will just not evaluate and return null if there is a null value somewhere in the chain. Using ?: called the Elvis-operator, allows to specify a default value in such a case. This helps us to avoid stacked null checks with if statements and also drop using Optionals, so we write more readable code and use null values in a much more precise way.
Data class
Creating classes that are meant to store data is usually simple. Declare members, decide whether they are mutable or not and politely ask your IDE to generate boilerplate code to match Java Bean convention. Koltin has a special functionality – data class, intended to make creating such classes even simpler and less error-prone.
data class MyClass(val foo: String, var boo: Int)
And that’s all folks! It’s the compiler, not IDE, that is responsible for taking properties from the primary constructor and using them to create all additional methods. This makes your data class basically a list of properties, without all generated functions that you would never bother to read.
But if there actually is a need for custom, fine-tuned, equals(), or else, you are free to put it in the class body. This way it is less likely to lose track of introduced exceptions in such code, that would usually be hidden among generic, auto-generated listings.
Another great generated function is copy(), that (duh) copies our instance. This is remarkably useful when we need to deal with an immutable data object, that changes over time, i.e. form state.
myClassInstance = myClassInstance.copy(foo = “foo”)
And that example takes us to…
Named properties
This may seem like a little thing, but I really missed it in Java. It is possible to name arguments for a function, greatly improving readability for functions with many arguments. When we provide default values, we can pass only parameters that we need. This is exactly what happened in the previous example, the compiler generates following implementation for the copy function:
fun copy(foo: String = this.foo, boo: Int = this.boo)
allowing us to pass only some of the new parameters, using old ones as defaults. This is also an excellent alternative for patterns like Builder, that create new objects, allowing default or optional properties.
Delegation
Delegation over inheritance. I believe it’s one of the most important and useful rules, regarding Object Oriented Programming. Delegation proved itself to be a much cleaner and flexible pattern for sharing implementation code, than plain inheritance. Kotlin aids it by allowing to specify implementation object, that will be used in auto-generated implementation functions.
interface MyInterface {
fun doStuff()
}
class CoolMyInterface: MyInterface {
fun doStuff() {
//Cool stuff
}
}
class MyDerivedClass: MyInterface(impl: MyInterface): MyInterface by impl
myDerived.doStuff() // Just does cool stuff, no boilerplate.
In this example, class MyDerivedClass implements MyInterface. Usually, you would have to implement all functions defined in MyInterface, then inside them, delegate execution to an object that actually implements them, lots of boilerplate.
As you probably know from previous examples, Kotlin compiler is pretty eager to take this burden away. It will automatically generate implemented functions, that will forward to corresponding functions of the object specified with by-clause.
Extension functions
Sometimes it is necessary to add extra ability to a class. Extending is usually not the cleanest way to do it, the cleanest approach is to use a design pattern, such as Decorator or writing static “Util” functions, depending on the use-case. But that requires quite a lot of ceremony, writing extra code. Languages like C# or Kotlin bypass that problem with extension functions.
fun MyCustomView.actionsFlowable(): Flowable<Action> =
MyCustomViewActionsFlowable(this)
myCustomViewInstance.actionsFlowable().subscribe()
This example shows how to declare a convenience function, that returns Flowable that wraps a traditional callback-based event listener. Function is prefixed with receiver type, the type that we are extending. When we call this on an actual object we will be able to refer to it as this inside the function body.
This way it is possible to add functionality to the class without extending it or wrapping it in decorators, maintaining the ability to call the function as a regular class member.
It is just smart and concise
Switching from Java to Kotlin involved a learning curve, as I needed to ditch some old habits and get used to new conventions. As soon as I got my head around it, I noticed I was writing much less code, compared to Java, to get the same results. For example, smart casts feature tracks already performed type checks and does not require explicit casting afterwards.
if(s is String) {
s.length //s is treated as String here
}
if(s is String && s.length > 0)
when(s) {
is String -> s.length + 1
is Int -> s + 1
}
As you can see, there is no need to cast already checked types, in Java, it often led to code like ((String) s).length, where casting was logically redundant.
Kotlin is also a thing that encouraged me to get deeper into functional programming.
It all started with RxJava, which heavily incorporates passing functions and successive function calls. Of course, Java itself introduced lambda expressions and functional interfaces, but the latter is restricted on older versions of Android SDK. Kotlin however, naturally treats functions as first-class, providing a compact notation for function types.
And I can still go on with the list, naming more and more syntactic sugar and little tricks and details. Ability to move lambda out of parentheses, when it is the last parameter of the function. How type-safe builders allow creating DSLs with ease. Or scoping functions that aid programmers in writing more human-readable and expressive code. Or if and when statements that can return values. Or coroutines, that can provide a different view on writing asynchronous code.
Do I need to switch from Java to Kotlin in Android?
Well, maybe you don’t need to, but you probably should. Now it’s the main language, recommended for Android by Google, so it will receive new libs and features first. It’s also interoperable with Java, so you can still use old libraries and tools.
As you might have guessed, I enjoyed my learning experience as much as I enjoy working with Kotlin now. I like conciseness and ability to achieve my goals with less code written.
Please note that my previous experience was mostly Java, language infamous for excessive amounts of code needed to be written. But that probably applies to the majority of Android devs, so there is a fair chance that you will benefit from the switch.
And if you don’t know how to code or want to focus entirely on your business operations, let us know about your software idea, and we will take care of it. Our team of specialists knows various programming languages and frameworks, which allows them to build robust, high-quality digital products. We can make one for you, too. Contact us, and let’s talk about your needs and requirements.