Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Kotlin on the JVM: How Can Kotlin Provide So Many Features?

DZone's Guide to

Kotlin on the JVM: How Can Kotlin Provide So Many Features?

Java can get a bit, well, wordy, at times. See how using Kotlin can reduce the amount of code you need to write thus increasing performance.

· Performance Zone
Free Resource

Introduction

What exactly is a "JVM language"? Isn’t only Java meant to be run on the JVM?
Kotlin provides many features that aren’t available in Java such as a proper function type, extension functions, or data classes. How was this even achievable? I’ve taken a deeper look at how Kotlin is made possible and what "JVM language" actually means. We'll be having a look at Kotlin's bytecode generation. I hope to help some people understand a few things better.

For a more detailed introduction to Kotlin’s features, you can have a look at my recent posts like this one.

The Java Virtual Machine

A quick and simple definition: The Java Virtual Machine is used by computers to run Java bytecode.
Actually, there’s a lot more to learn about this complex tool, which is described extensively in Oracle’s Spec.

As you might already know, the JVM is an abstract virtual computer running on various operating systems. In fact, the JVM is what makes Java "platform independent," because it acts as an abstraction between the executed code and the OS.

Just like any real computer, the JVM provides a defined set of instructions which can be used by a program and are translated to machine specific instructions by the JVM itself later on.

As described in the JVM Spec, the Java Virtual Machine doesn’t know anything about the programming language Java. However, it defines the binary format class which is a file containing machine instructions (= bytecodes) to be executed (besides some more information). This is a very interesting point because it actually means, that:

  1. the JVM isn’t only dedicated to Java as a programming language.
  2. you are free to choose a technology for creating JVM programs as long as you provide proper class files that are compliant to the very strict constraints.
  3. regardless of programming languages, any Java bytecode can interoperate with other Java bytecode on the JVM.

Creation of Class Files

The process of creating class files from human-readable source code is what a compiler does. One example is Oracle’s Java Compiler shipped with the JDK (javac) that is capable of compiling .java files to .class files.

In Addition to Java, many other JVM languages have emerged in the last few years, which are supposed to provide an alternative abstraction for us developers to create programs for the JVM.
One of these languages is Kotlin.

Kotlin Bytecode Generation

As stated in the official FAQs "Kotlin produces Java compatible bytecode," which means that the Kotlin compiler is capable of transforming all the nice features into JVM compatible instructions and this can even be observed using IntelliJ IDEA tools.

Let’s look at some examples:

Top Level Functions

Kotlin

//File.kt
fun foobar(){}

This simple top level function defined in a .kt file can be investigated with IntelliJ:
"Tools → Kotlin → Show Kotlin Bytecode" will open a new window inside the IDE providing a live preview of the Java bytecode the compiler would create for the currently edited .kt file.

Java bytecode

public final class de/swirtz/kotlin/FileKt {
  // access flags 0x19
  public final static foobar()V
   L0
    LINENUMBER 3 L0
    RETURN
   L1
    MAXSTACK = 0
    MAXLOCALS = 0

  @Lkotlin/Metadata;
  // compiled from: File.kt
}

I’m afraid only a few people can actually read these files, which is why we can also choose the option "Decompile." Afterward, we’ll be presented a Java class enclosing the functionality previously described with Kotlin:

Top Level Function decompiled

public final class FileKt {
   public static final void foobar() {
   }
}

As you can see and probably already know, a Kotlin top level class is compiled into a final Java class with a static function (This structure looks like what extension functions mean to replace: utility classes). Let’s see a more difficult one:

Classes and Extension Functions

Kotlin

class MyClass(val i: Int)

fun MyClass.myExtension(value: String) = value.length

This one shows a simple class MyClass with a property of type Int, as well as a top level extension function.

First, we should have a look at what the class is compiled to, which is quite interesting as we used a primary constructor and the val keyword here.

Class in Java

public final class MyClass {
   private final int i;

   public final int getI() {
      return this.i;
   }

   public MyClass(int i) {
      this.i = i;
   }
}

As we would expect: the property is a final member being assigned in the single constructor. Yet, so much simpler in Kotlin.

Extension Function decompiled

public final class FileKt {
   public static final int myExtension(@NotNull MyClass $receiver, @NotNull String value) {
      Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
      Intrinsics.checkParameterIsNotNull(value, "value");
      return value.length();
   }
}

The extension function itself is compiled to a static method with its receiver object as a parameter in the Java code.

One thing we can also observe in the example is the use of a class called Intrinsics. This one is part of the Kotlin stdlib and is utilized because the parameters are required to be not null.
Let’s see what would happen if we changed the initial extension function’s parameter to value: String? and, of course, access length in a safe way.

Extension Function with nullable Parameter decompiled

public final class FileKt {
   @Nullable
   public static final Integer myExtension(@NotNull MyClass $receiver, @Nullable String value) {
      Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
      return value != null?Integer.valueOf(value.length()):null;
   }
}

Checking value is not necessary anymore since we told the compiler that null is an acceptable thing to point to.

The next example is a bit more tricky. It’s the one with the greatest difference between Kotlin and Java code:

Ranges  

Kotlin

fun loopWithRange(){
    for(i in 5 downTo 1 step 2){
        print(i)
    }
}

decompiled

 public static final void loopWithRange() {
      IntProgression var10000 = RangesKt.step(RangesKt.downTo(5, 1), 2);
      int i = var10000.getFirst(); //i: 5
      int var1 = var10000.getLast(); //var1: 1
      int var2 = var10000.getStep(); //var2: -2
      if(var2 > 0) {
         if(i > var1) {
            return;
         }
      } else if(i < var1) {
         return;
      }

      while(true) {
         System.out.print(i);
         if(i == var1) {
            return;
         }

         i += var2;
      }
   }

Although the Java code still is quite comprehensible, probably nobody would write it in real life, because a simple for could do it, too. We need to consider that downTo and step are infix notations, which are actually function calls. In order to provide this flexibility, a little more code just seems to be necessary.

What do you think? It doesn't look that nice, although the Kotlin code is brilliant, right?

Conclusion

I think, most of the time we don’t really care about what the Kotlin compiler produces for us. Yet, I find observing it really interesting and helpful as it supports answering my initial questions in some way. Of course, Kotlin is much more than just abstracting Java’s operators since it also provides so many extensions to existing Java classes like List or String.
Nevertheless, we also saw, that sometimes the compiled Java code is more verbose than it had to be. Could this impact performance? Yes indeed, it does have minor effects. Have a look at this presentation by Dmitry Jemerov if you’re interested in more "Kotlin → Java bytecode" examples that take performance into consideration, too.

Finally, if you want to read about Kotlin's beautiful features I recommend the book Kotlin in Action to you!

Let me know what you think about it and get in touch if you like!

 - Simon

Topics:
java ,kotlin ,jvm ,bytecode ,performance

Published at DZone with permission of Simon Wirtz. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}