答え
Javaの観点からは、インターフェースTrait.scala
にコンパイルされます。したがって、Javaでの実装は、インターフェースの実装として解釈されます。これにより、エラーメッセージが明確になります。簡単な答え:Javaでの多重継承が可能になるため、Javaでのトレイト実装を利用することはできません(!)Trait
Trait
Scalaではどのように実装されていますか?
長い答え:では、Scalaではどのように機能するのでしょうか?生成されたバイトコード/クラスを見ると、次のコードが見つかります。
interface Trait {
void bar();
}
abstract class Trait$class {
public static void bar(Trait thiz) {/*trait implementation*/}
}
class Foo implements Trait {
public void bar() {
Trait$class.bar(this); //works because `this` implements Trait
}
}
Trait
インターフェースです
- 抽象
Trait$class
(と混同しないでくださいTrait.class
)クラスは透過的に作成され、技術的にはインターフェースを実装しません。Trait
ただし、インスタンスを引数としてstatic bar()
取るメソッドがありTrait
ます(一種のthis
)
Foo
Trait
インターフェイスを実装します
scalac
Trait
に委任することにより、メソッドを自動的に実装しTrait$class
ます。これは本質的にを呼び出すことを意味しTrait$class.bar(this)
ます。
Trait$class
はのメンバーではなく、拡張Foo
もしないことに注意してください。Foo
を渡すことでそれに委任するだけthis
です。
複数の特性を混ぜる
Scalaがどのように機能するかについての余談を続けるために...そうは言っても、複数の特性の混合がその下でどのように機能するかを想像するのは簡単です。
trait Trait1 {def ping(){}};
trait Trait2 {def pong(){}};
class Foo extends Trait1 with Trait2
次のように変換されます。
class Foo implements Trait1, Trait2 {
public void ping() {
Trait1$class.ping(this); //works because `this` implements Trait1
}
public void pong() {
Trait2$class.pong(this); //works because `this` implements Trait2
}
}
同じメソッドをオーバーライドする複数の特性
これで、同じメソッドをオーバーライドする複数の特性を混合する方法を想像するのは簡単です。
trait Trait {def bar(){}};
trait Trait1 extends Trait {override def bar(){}};
trait Trait2 extends Trait {override def bar(){}};
繰り返しますが、拡張するインターフェイスTrait1
にTrait2
なりますTrait
。Trait2
定義するときに最後に来る場合Foo
:
class Foo extends Trait1 with Trait2
あなたが得るでしょう:
class Foo implements Trait1, Trait2 {
public void bar() {
Trait2$class.bar(this); //works because `this` implements Trait2
}
}
ただし、切り替えTrait1
てTrait2
(Trait1
最後にする)と、次のようになります。
class Foo implements Trait2, Trait1 {
public void bar() {
Trait1$class.bar(this); //works because `this` implements Trait1
}
}
積み重ね可能な変更
次に、積み重ね可能な変更としての特性がどのように機能するかを検討します。本当に便利なクラスFooがあると想像してみてください。
class Foo {
def bar = "Foo"
}
トレイトを使用していくつかの新機能で強化したいもの:
trait Trait1 extends Foo {
abstract override def bar = super.bar + ", Trait1"
}
trait Trait2 extends Foo {
abstract override def bar = super.bar + ", Trait2"
}
これがステロイドの新しい「Foo」です:
class FooOnSteroids extends Foo with Trait1 with Trait2
これは次のように解釈されます。
特性1
interface Trait1 {
String Trait1$$super$bar();
String bar();
}
abstract class Trait1$class {
public static String bar(Trait1 thiz) {
// interface call Trait1$$super$bar() is possible
// since FooOnSteroids implements Trait1 (see below)
return thiz.Trait1$$super$bar() + ", Trait1";
}
}
特性2
public interface Trait2 {
String Trait2$$super$bar();
String bar();
}
public abstract class Trait2$class {
public static String bar(Trait2 thiz) {
// interface call Trait2$$super$bar() is possible
// since FooOnSteroids implements Trait2 (see below)
return thiz.Trait2$$super$bar() + ", Trait2";
}
}
FooOnSteroids
class FooOnSteroids extends Foo implements Trait1, Trait2 {
public final String Trait1$$super$bar() {
// call superclass 'bar' method version
return Foo.bar();
}
public final String Trait2$$super$bar() {
return Trait1$class.bar(this);
}
public String bar() {
return Trait2$class.bar(this);
}
}
したがって、スタック呼び出し全体は次のようになります。
- FooOnSteroidsインスタンス(エントリポイント)の「bar」メソッド。
- Trait2 $classの'bar'静的メソッドはこれを引数として渡し、' Trait2 $$ super $ bar()'メソッド呼び出しと文字列 "、Trait2"の連結を返します。
- ..を呼び出すFooOnSteroidsインスタンスの「Trait2$$super $ bar()」
- Trait1 $classの'bar'静的メソッドはこれを引数として渡し、' Trait1 $$ super $ bar()'メソッド呼び出しと文字列 "、Trait1"の連結を返します。
- ..を呼び出すFooOnSteroidsインスタンスの「Trait1$$super$bar」
- オリジナルのFooの「bar」メソッド
そして、結果は「Foo、Trait1、Trait2」です。
結論
すべてを読むことができた場合、元の質問に対する答えは最初の4行にあります...