おもしろい順に記事を投稿していたら、とっても古くなってしまいました。(´・_・`) しかし、内容自体は古くなってはいないと思うのでそのまま投稿します。(ง ˘ω˘ )ว
Scala2.12とかJava8とかデフォルト実装付きinterfaceとかは知りません(・ ω ・)
traitの実装
Scalaが実際にはどのようにtraitを実現しているのかを調べるために、
object A { trait X { def xx() = "xx" } def main(args: Array[String]) { val x = new X {} } }
がどうなっているかを調べました。
生成されるファイル
> ls target/scala-2.11/classes/
をしてみると
- A.class
- A$.class
- A$X.class
- A$X$class.class
- A$$anon$1.class
が生成されていることが分かりました。
具体的な内容
A.class
とA$.class
はobject A
の実装を担当しています。(mainメソッドとかもここに)
次に、A$X
を
javap -p A$X
で調べると
public interface A$X { public abstract java.lang.String xx(); }
となっていました。
同様に、A$X$class
は
public abstract class A$X$class { public static java.lang.String xx(A$X); Code: 0: ldc #8 // String xx 2: areturn public static void $init$(A$X); Code: 0: return }
となっていてtrait X
はコンパイル時にinterfaceとabstract classに分離するらしいということが分かりました。(最適化とかいろいろあると思うのでいつもこうとは限らないかもしれません・・。)
そしてnew X {}
を担当するA$$anon$1
は
public final class A$$anon$1 implements A$X { public java.lang.String xx(); Code: 0: aload_0 1: invokestatic #19 // Method A$X$class.xx:(LA$X;)Ljava/lang/String; 4: areturn public A$$anon$1(); Code: 0: aload_0 1: invokespecial #25 // Method java/lang/Object."<init>":()V 4: aload_0 5: invokestatic #29 // Method A$X$class.$init$:(LA$X;)V 8: return }
となっていました。これをよく見てみると、
1.tarit X
のinterfaceであるA$X
をimplementsする
2.A$Xインターフェース
で必要になっているxx
を自分で実装する(A$X$class
をoverrideするのではなく)
3.A$X$class
のxxメソッド
がstaticになっているのでinvokestaticで呼び出す
のような順番で呼ばれていることが分かります。
staticだと困るんじゃないかと思いましたが、
A$X$class
のxxメソッド
がいつのまにか public static java.lang.String xx(A$X)となっていて引数にA$X
を受け取るようになっています。
実験ベースなので普遍性があるのかやや不明ですが、とりあえず今回の例ではこのようになることが分かりました。