4
votes

In Java you cannot extend a final class, but in Kotlin you can write extension method to such final classes. How did they make it work internally? I couldn't find any article that explained the inner workings of Kotlin Extension Methods.

2
they are just converted to static methods, more of a syntactic sugar really.Daniel
Are you using Intellij IDEA or Android Studio? Open a Kotlin file that has extension functions, go to Tools -> Kotlin -> Show bytecode, and from the bytecode window that pops up choose Decompile. You'll see what the Java analog of that code looks like and it'll quickly become apparent what the trick is!Egor
You can refer to this link - kotlinlang.org/docs/reference/….Rajesh kumar

2 Answers

8
votes

In Java you cannot extend a final class, but in Kotlin you can write extension method to such final classes

I think you are assuming extension methods used inheritance which is not. It is rather a static method disguised in kotlin syntactic sugar. For example look at the below simple extension method:

String.removeSpace(): String {
    return this.replace(" ", "")
}

You can think it as equivalent to the following java code.

static String removeSpace(String input) {
    return input.replace(" ", "")
}

What kotlin does here is it provides wrapper on top so that by using this you can get the instance of the caller. By combining that with Kotlin's capability of define function as first class citizen, you can write elegant thing like this:

fun String.removeSpace() = this.replace(" ", "")
4
votes

If you wonder how Kotlin code covert into Java code, there is a way to look at it. IntelliJ provides decompile Kotlin byte code to Java code. You can find it Tools -> Kotlin -> Show Kotlin Bytecode and hit the Decompile. Then it will show Java code.

Here is an example.

Kotlin

val Int.asByteArray get() =
    byteArrayOf(
            (this shr 24).toByte(),
            (this shr 16).toByte(),
            (this shr 8).toByte(),
            this.toByte())

val Int.asHex get() = this.asByteArray.asHexUpper

@ExperimentalUnsignedTypes
val ByteArray.asInt get() =
        ((this[0].toUInt() and 0xFFu) shl 24) or
        ((this[1].toUInt() and 0xFFu) shl 16) or
        ((this[2].toUInt() and 0xFFu) shl 8) or
        (this[3].toUInt() and 0xFFu)

Java

public final class IntExtensionKt {
   @NotNull
   public static final byte[] getAsByteArray(int $this$asByteArray) {
      return new byte[]{(byte)($this$asByteArray >> 24), (byte)($this$asByteArray >> 16), (byte)($this$asByteArray >> 8), (byte)$this$asByteArray};
   }

   @NotNull
   public static final String getAsHex(int $this$asHex) {
      return HexExtensionKt.getAsHexUpper(getAsByteArray($this$asHex));
   }

   /** @deprecated */
   // $FF: synthetic method
   @ExperimentalUnsignedTypes
   public static void asInt$annotations(byte[] var0) {
   }

   public static final int getAsInt(@NotNull byte[] $this$asInt) {
      Intrinsics.checkParameterIsNotNull($this$asInt, "$this$asInt");
      byte var1 = $this$asInt[0];
      boolean var2 = false;
      int var5 = UInt.constructor-impl(var1);
      short var6 = 255;
      boolean var3 = false;
      var5 = UInt.constructor-impl(var5 & var6);
      byte var7 = 24;
      var3 = false;
      var5 = UInt.constructor-impl(var5 << var7);
      byte var8 = $this$asInt[1];
      var3 = false;
      int var9 = UInt.constructor-impl(var8);
      short var10 = 255;
      boolean var4 = false;
      var9 = UInt.constructor-impl(var9 & var10);
      byte var11 = 16;
      var4 = false;
      var9 = UInt.constructor-impl(var9 << var11);
      var3 = false;
      var5 = UInt.constructor-impl(var5 | var9);
      var8 = $this$asInt[2];
      var3 = false;
      var9 = UInt.constructor-impl(var8);
      var10 = 255;
      var4 = false;
      var9 = UInt.constructor-impl(var9 & var10);
      var11 = 8;
      var4 = false;
      var9 = UInt.constructor-impl(var9 << var11);
      var3 = false;
      var5 = UInt.constructor-impl(var5 | var9);
      var8 = $this$asInt[3];
      var3 = false;
      var9 = UInt.constructor-impl(var8);
      var10 = 255;
      var4 = false;
      var9 = UInt.constructor-impl(var9 & var10);
      var3 = false;
      return UInt.constructor-impl(var5 | var9);
   }
}

As you can see, a Kotlin extension method became public final static method in final class in Java.