2016/08/13 このエントリーをはてなブックマークに追加 はてなブックマーク - 「Show Kotlin Bytecode」でKotlinを理解する

「Show Kotlin Bytecode」でKotlinを理解する


今回はKotlinを知るために便利な、もしくは困ったときにヒントになるであろう、
Kotlin BytecodeをIntelliJ IDEAで見ることの出来る機能を紹介します。
今更感のあるTips的なやつです。

※私の確認環境はIntelliJ IDEA 15.0.6 CEです。


まず、Kotlinのコードを書きます。
main関数でhello,worldですね。極めて一般的です。

fun helloworld() {
    println("コンニチワセカイ")
}

Tools>Kotlin>Show Kotlin Bytecodeを選びます。
もしくはshift2回でsearch everywhereからも選択可能です。

そうすると、なんと、Kotlinのコンパイル後のバイトコードが現れます!!
kotlincしてJavaのクラスファイルになったものをjavapしたものって感じです。

さらに、右上のDecompileというボタンを押すと。。。







KotlinがJava側からどう見えるかが分かります!


この機能を使いながらKotlinのコードを書くことは以下の様なメリットがあると思います。

  • Javaから呼び出しやすい
  • 自分の書いたKotlinのコードの意味を理解しやすい
  • より有効なコードを書くヒントになる
  • 良くないコーディングやハマりどころを解決するヒントになる

例えば、簡単なクラスを作ってみましょう。

class Person(val age : Int, val name : String)
これがJavaにdecompileするとこうなります。
import kotlin.Metadata;
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;

@Metadata(
   mv = {1, 1, 1},
   bv = {1, 0, 0},
   k = 1,
   d1 = {"\u0000\u0018\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0010\b\n\u0000\n\u0002\u0010\u000e\n\u0002\b\u0006\u0018\u00002\u00020\u0001B\u0015\u0012\u0006\u0010\u0002\u001a\u00020\u0003\u0012\u0006\u0010\u0004\u001a\u00020\u0005¢\u0006\u0002\u0010\u0006R\u0011\u0010\u0002\u001a\u00020\u0003¢\u0006\b\n\u0000\u001a\u0004\b\u0007\u0010\bR\u0011\u0010\u0004\u001a\u00020\u0005¢\u0006\b\n\u0000\u001a\u0004\b\t\u0010\n¨\u0006\u000b"},
   d2 = {"LPerson;", "", "age", "", "name", "", "(ILjava/lang/String;)V", "getAge", "()I", "getName", "()Ljava/lang/String;", "production sources for module core"}
)
public final class Person {
   private final int age;
   @NotNull
   private final String name;

   public final int getAge() {
      return this.age;
   }

   @NotNull
   public final String getName() {
      return this.name;
   }

   public Person(int age, @NotNull String name) {
      Intrinsics.checkParameterIsNotNull(name, "name");
      super();
      this.age = age;
      this.name = name;
   }
}


これだけでも

  • メタデータを持っていること
  • getterが自動で生成されること
  • クラスも、フィールドもfinalであること
  • @NotNullによるチェックが行われていること

などが分かります!




そしてさっきのコードにdataを付け加えましょう。


data class Person(val age : Int, val name : String)



そうすると、こうなります。
import kotlin.Metadata;
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;

@Metadata(
   mv = {1, 1, 1},
   bv = {1, 0, 0},
   k = 1,
   d1 = {"\u0000\u0018\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0010\b\n\u0000\n\u0002\u0010\u000e\n\u0002\b\t\b\u0086\b\u0018\u00002\u00020\u0001B\u0015\u0012\u0006\u0010\u0002\u001a\u00020\u0003\u0012\u0006\u0010\u0004\u001a\u00020\u0005¢\u0006\u0002\u0010\u0006J\t\u0010\u000b\u001a\u00020\u0003HÆ\u0003J\t\u0010\f\u001a\u00020\u0005HÆ\u0003J\u001d\u0010\r\u001a\u00020\u00002\b\b\u0002\u0010\u0002\u001a\u00020\u00032\b\b\u0002\u0010\u0004\u001a\u00020\u0005HÆ\u0001R\u0011\u0010\u0002\u001a\u00020\u0003¢\u0006\b\n\u0000\u001a\u0004\b\u0007\u0010\bR\u0011\u0010\u0004\u001a\u00020\u0005¢\u0006\b\n\u0000\u001a\u0004\b\t\u0010\n¨\u0006\u000e"},
   d2 = {"LPerson;", "", "age", "", "name", "", "(ILjava/lang/String;)V", "getAge", "()I", "getName", "()Ljava/lang/String;", "component1", "component2", "copy", "production sources for module core"}
)
public final class Person {
   private final int age;
   @NotNull
   private final String name;

   public final int getAge() {
      return this.age;
   }

   @NotNull
   public final String getName() {
      return this.name;
   }

   public Person(int age, @NotNull String name) {
      Intrinsics.checkParameterIsNotNull(name, "name");
      super();
      this.age = age;
      this.name = name;
   }

   public final int component1() {
      return this.age;
   }

   @NotNull
   public final String component2() {
      return this.name;
   }

   @NotNull
   public final Person copy(int age, @NotNull String name) {
      Intrinsics.checkParameterIsNotNull(name, "name");
      return new Person(age, name);
   }

   // $FF: synthetic method
   // $FF: bridge method
   @NotNull
   public static Person copy$default(Person var0, int var1, String var2, int var3, Object var4) {
      if(var4 != null) {
         throw new UnsupportedOperationException("Super calls with default arguments not supported in this target, function: copy");
      } else {
         if((var3 & 1) != 0) {
            var1 = var0.age;
         }

         if((var3 & 2) != 0) {
            var2 = var0.name;
         }

         return var0.copy(var1, var2);
      }
   }

   public String toString() {
      return "Person(age=" + this.age + ", name=" + this.name + ")";
   }

   public int hashCode() {
      return this.age * 31 + (this.name != null?this.name.hashCode():0);
   }

   public boolean equals(Object var1) {
      if(this != var1) {
         if(var1 instanceof Person) {
            Person var2 = (Person)var1;
            if(this.age == var2.age && Intrinsics.areEqual(this.name, var2.name)) {
               return true;
            }
         }

         return false;
      } else {
         return true;
      }
   }
}



component1,component2,copy,equals,hashcodeなど
色々なメソッドが増えていることが分かりますね!


他にもsealed classやenum class、when式や拡張関数、object、delegateなんてものもあります。
実際どのような動きをしているのかIntelliJ IDEAで確かめてみてください!


KotlinをJavaで理解する by Jumpei Yamamoto

kotlinはJavaからどう見えるか? @boohbah



0 件のコメント:

コメントを投稿