KotlinのisはJavaのinstanceofだよ

各位

お疲れ様です。 表題の通りKotlinのisはJavaのinstanceofを内部的には使用しています。

以上よろしくお願いいたします。


というわけで、Kotlinのパターンマッチにつかうisが内部的にどのような処理を行っているのかが気になったので調べてみました。 方法としてはKotlinでisを使ったコードをコンパイルしてできたクラスファイルを Javaコンパイラデコンパイルします。

Kotlinのコンパイラのバージョンは'1.3.11 デコンパイラにはCFR v0.139を使用しました。

Kotlinのコード

class A(){
    val a="aaa"
}
fun test(hoge:Int){
    val b= if(hoge==0)A()else null
    if(b is A){
        print("a")
    }else{
        print("b")
    }

}

これをコンパイルして得たTestKt.classデコンパイルした結果が以下のとおりです。

コマンド

java -jar cfr-0.139.jar   out/production/classes/TestKt.class 
/*
 * Decompiled with CFR 0.139.
 * 
 * Could not load the following classes:
 *  kotlin.Metadata
 */
import java.io.PrintStream;
import kotlin.Metadata;

@Metadata([省略])
public final class TestKt {
    public static final void test(int hoge) {
        A b;
        A a = b = hoge == 0 ? new A() : null;
        if (b instanceof A) {
            String string = "a";
            System.out.print((Object)string);
        } else {
            String string = "b";
            System.out.print((Object)string);
        }
    }
}

みごとにinstanceofが登場していることがわかりますね。 Javaではinstanceofを使うのはあまり良くないとされていると思いますが、 Kotlinではスマートキャストのおかげか大活躍していますね。

ちなみにinstanceofの内部の動きに関してはJJUG CCC Fall 2018で発表された方がいて 大変勉強になりました。以下のリンクがその時のスライドです。 参考にどうぞ

Deep dive into instanceof

楽しく見ていただいた方はチャンネル登録お願いします。(?)


追記

スマートキャストが動いている例もやってみました。


sealed class A{
    class B(var num:Int):A()
    class C(var str:String):A()
    class D(var double: Double,var int: Int):A()

}
fun test(hoge:Int){
    val v:A= if(hoge==0){
        A.B(1)
    }else if(hoge==1){
        A.C("1")
    } else {
        A.D(1.0,2)
    }
    when(v){
        is A.B->
            print(v.num)
        is A.C->
            print(v.str)
        is A.D->
            print(listOf(v.double,v.int))
    }

}

逆翻訳後

/* クラス定義は省略します */
public final class TestKt {
    public static final void test(int hoge) {
        A v = hoge == 0 ? (A)new A.B(1) : (hoge == 1 ? (A)new A.C("1") : (A)new A.D(1.0, 2));
        A a = v;
        if (a instanceof A.B) {
            int n = ((A.B)v).getNum();
            System.out.print(n);
        } else if (a instanceof A.C) {
            String string = ((A.C)v).getStr();
            System.out.print((Object)string);
        } else if (a instanceof A.D) {
            List list = CollectionsKt.listOf((Object[])new Object[]{((A.D)v).getDouble(), ((A.D)v).getInt()});
            System.out.print(list);
        }
    }
}

普通にダウンキャストと同じバイトコードになるようですね。