とある事情でJavaからScala用のライブラリを使わなければならなくなりました。
そのライブラリはあるTraitをミックスインすることで、機能を自分でカスタマイズできます。そして今回、そのカスタマイズが必要になったため、JavaでそのTraitをimplementsしようと試みました。
しかしすんなりとはいかず苦戦を強いられました。そこでJavaではTraitとはどういった扱いになるのかをちょっと調べることにしました。
JavaでTraitをimplements
まずこのScala用ライブラリの機能をカスタマイズするためにTraitをJava側で使うことにしました。
そのTraitは下記のようなものでした。(実際のソースコードではありません)
trait Trait_1 {
val a: String
val b: Int
}
trait Trait_1_1 extends Trait_1{
val a: String = "a"
val b: Int = 1
}
JavaではTraitというものはありませんが、書籍でTraitはinterfaceのようなものとあったのでとあったのでclassにimplementsしてみました。
public class Class_1 implements Trait_1_1 {
}
しかし、下記のエラーが。
Class 'Class_1' must either be declared abstract or implement abstract method 'a()' in 'Trait_1_1'
しかしTrait_1_1にはa()なんてメソッドはありません。
このメソッドなんだ?どこから出てきたんだ?
そういえばscalaではフィールドへのgetterやsetterが暗黙的に作られると聞いた気がする。
ここでようやくclassファイルをデコンパイルすることを思いつきました。
Traitをデコンパイル
Traitの中身を知るために、ScalaでTraitを実装してコンパイルした後、作成されたclassファイルをjadを使ってデコンパイルしてみました。
まずはval変数を宣言しただけのTrait_1をデコンパイル。
public interface Trait_1
{
public abstract String a();
public abstract int b();
}
おお、a()メソッドが出てきましたね。これはおそらくgetterメソッドでしょう。
あとinterfaceになりました。
次に、Trait_1_1ですが、こちらはコンパイルしたところ、
Trait_1_1.class
Trait_1_1$class.class
と2つののclassファイルが作成されました。
それぞれをデコンパイルした結果がこちら。
public interface Trait_1_1
{
public abstract void Trait_1_1$_setter_$a_$eq(String s);
public abstract void Trait_1_1$_setter_$b_$eq(int i);
public abstract String a();
public abstract int b();
}
public abstract class Trait_1_1$class
{
public static void $init$(Trait_1_1 $this)
{
$this.Trait_1_1$_setter_$a_$eq("a");
$this.Trait_1_1$_setter_$b_$eq(1);
}
}
Trait_1_1はinterfaceに、Trait_1_1$classはabstractクラスになりました。
Trait_1_1には新しいメソッドが出てきましたね。setterと書いてあることから、setterメソッドでしょう。
Trait_1_1$classにはそのsetterメソッドを使用したinitメソッドがあります。scalaでval変数の定義をしたことが、javaではこういう形になるようです。
ここまでの結果をまとめると、Trait_1はval変数の宣言しかせず、何の値も定義していないためgetterだけ、Trait_1_1は値を定義したためsetterが出てきたということでしょうか。
そして、これらのメソッドはabstractなので、実装を書かなければならないというのがエラーの原因だったようです。
ではこれらのabstractメソッドの実装をすればいけるのでしょうか?
それをしたのがこちら。
public class Class_1 implements Trait_1_1 {
public String a () {
return "a";
}
public int b () {
return 1;
}
public void Trait_1_1$_setter_$a_$eq(String s){};
public void Trait_1_1$_setter_$b_$eq(int i){};
}
getteメソッドは値を返すように、setterメソッドはひとまず中身なしのメソッドにしてみました。
一応これでコンパイルが通るようにはなりました。