ScalaのTraitをjavapしてみたメモ

Traitがどんな感じでJavaバイトコードに落ちているのか興味があったので試してみた。

komamitsu@carrot ~/lab/scala/traitsample2 $ scala -version
Scala code runner version 2.9.1.final -- Copyright 2002-2011, LAMP/EPFL
trait T1 {
  val age:Int
  val name:String
}

trait T2 {
  def hello(name:String) = println("hello " + name)
}

class C extends T1 with T2 {
  val age = 42
  val name = "komamitsu"
}

でjavapした結果がこれ

komamitsu@carrot ~/lab/scala/traitsample2 $ javap -private -c T1
Compiled from "Hello.scala"
public interface T1{
public abstract int age();

public abstract java.lang.String name();
}

komamitsu@carrot ~/lab/scala/traitsample2 $ javap -private -c T2
Compiled from "Hello.scala"
public interface T2 extends scala.ScalaObject{
public abstract void hello(java.lang.String);
}

komamitsu@carrot ~/lab/scala/traitsample2 $ javap -private -c T2\$class
Compiled from "Hello.scala"
public abstract class T2$class extends java.lang.Object{
public static void hello(T2, java.lang.String);
  Code:
   0:   getstatic       #11; //Field scala/Predef$.MODULE$:Lscala/Predef$;
   3:   new     #14; //class scala/collection/mutable/StringBuilder
   6:   dup
   7:   invokespecial   #18; //Method scala/collection/mutable/StringBuilder."<init>":()V
   10:  ldc     #20; //String hello 
   12:  invokevirtual   #24; //Method scala/collection/mutable/StringBuilder.append:(Ljava/lang/Object;)Lscala/collection/mutable/StringBuilder;
   15:  aload_1
   16:  invokevirtual   #24; //Method scala/collection/mutable/StringBuilder.append:(Ljava/lang/Object;)Lscala/collection/mutable/StringBuilder;
   19:  invokevirtual   #28; //Method scala/collection/mutable/StringBuilder.toString:()Ljava/lang/String;
   22:  invokevirtual   #32; //Method scala/Predef$.println:(Ljava/lang/Object;)V
   25:  return

public static void $init$(T2);
  Code:
   0:   return
}

komamitsu@carrot ~/lab/scala/traitsample2 $ javap -private -c C
Compiled from "Hello.scala"
public class C extends java.lang.Object implements T1,T2,scala.ScalaObject{
private final int age;

private final java.lang.String name;

public void hello(java.lang.String);
  Code:
   0:   aload_0
   1:   aload_1
   2:   invokestatic    #15; //Method T2$class.hello:(LT2;Ljava/lang/String;)V
   5:   return

public int age();
  Code:
   0:   aload_0
   1:   getfield        #23; //Field age:I
   4:   ireturn

public java.lang.String name();
  Code:
   0:   aload_0
   1:   getfield        #26; //Field name:Ljava/lang/String;
   4:   areturn

public C();
  Code:
   0:   aload_0
   1:   invokespecial   #32; //Method java/lang/Object."<init>":()V
   4:   aload_0
   5:   invokestatic    #36; //Method T2$class.$init$:(LT2;)V
   8:   aload_0
   9:   bipush  42
   11:  putfield        #23; //Field age:I
   14:  aload_0
   15:  ldc     #38; //String komamitsu
   17:  putfield        #26; //Field name:Ljava/lang/String;
   20:  return
}

ということで、Java的には以下のような感じになっているみたい。

  • valなどのメンバー変数について
    • アクセサはTraitのInterface側に
    • 変数の実体は継承したClass側に作成される
  • defについて
    • TraitのInterface側に呼び出し口が作成される
    • 実装はInterfaceを継承しているClass側に作成される(この辺はJavaの普通のInterfaceの実装と同じ)
      • ただし実質的な処理はTrait InterfaceのInner Classにstatic methodが出来ているので、それを呼び出すだけ