Writing Android apps in Kotlin is nice, as it’s easy to learn, especially when we know Java. Yet there are some traps we can fall into. Today, I would like to show you one related to Kotlin properties. We will also look into the bytecode.
Order of Class Properties
Actually, in most cases, tools embedded in IDEs like Android Studio or Intellij are very helpful but sometimes they can mislead us. Let’s assume that we have the following class:
It’s quite simple, isn’t it? But there is a snag in this code, because when we call
println(Foo().product) we will get
0 instead of
isValid seems to be
true, in actuality when the
product is evaluated, the
isValid property isn’t initialized yet and returns
Even the lint shows us a misleading hint. When we make a quickfix using Alt+Enter and selecting
Simplify expression, we will get
get() = 1 so the code behavior will change.
By moving the
isValid above the
product property we will get the expected result. It’s obvious now but we have to remember that properties order does matter and sometimes it can cause a lot of problems during refactoring.
Show Kotlin Bytecode!
But let’s go deeper. In the Intellij/Android Studio, we have a very useful tool which can show us the generated bytecode:
What is even more interesting is the
Decompile button, which is very helpful when we want to learn Kotlin by analyzing how the equivalent code written in Java would appear.
Let’s take our first example from this article and generate a decompiled version:
When we save this as a new Java class and call
println(Foo().product) we will get
2, which is different from the first result printed by Kotlin code. So, are these 2 code samples compiled to the same bytecode? Obviously not. The
Decompile button takes bytecode generated by Kotlin and tries to decompile it to regular Java code. So where does the difference come from? Let’s compare them! The simplest way to do this is to compile
Foo.kt using CLI compilers.
First, we should create 2 directories, for example
java, and place the
Foo.java there, respectively. Now, in each folder we should compile our sources to class files and use the
javap tool to show bytecode.
When we compare these 2 generated files, we will detect that there are 2 noticeable differences. But the important place is function
As we can see, the bytecode of Java class in the
getNumber function uses
iconst_1, which loads value
1 onto the stack. But, in the case of Kotlin code,
ifeq checks whether
isValid field is
0 (false) and, if so, it goes to
iconst_0, otherwise it goes to
1 is returned respectively.
Let’s break something
So, we know now that the
Decompile option can show us Java code which gives us a different result than our Kotlin code. However, can we generate Java code which doesn’t compile? Of course! Let’s consider this case:
The sample above is 100% correct Kotlin code which compiles to regular JVM bytecode:
In this example, we use backquotes to name a property using spaces. But, when we choose the
Decompile option in the tool mentioned above, we get the output which isn’t proper Java code, because Java doesn’t allow you to use any spaces in the fields. Moreover, the generated method
is_valid() is repeated, which is also incorrect.
Is worth remembering that we should always be careful about property order in Kotlin, but also be aware of how tools embedded in our IDE work. Although both Java and Kotlin are JVM languages, not every JVM bytecode can be decompiled to compilable Java code.