Property Order in Kotlin – Why It Matters

Szymon KozakAndroid Developer

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 2. Although isValid seems to be true, in actuality when the product is evaluated, the isValid property isn’t initialized yet and returns false, so number returns 0.

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.java and Foo.kt using CLI compilers.

First, we should create 2 directories, for example kotlin and java, and place the Foo.kt and 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 getNumber.

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 iconst_1. Finally 0 or 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:

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.

Conclusion

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.