62

JavaからScalaトレイトに実装されたメソッドを呼び出すことはできないと思いますか、それとも方法はありますか?

私がScalaにいるとしましょう:

trait Trait {
  def bar = {}
}

そしてJavaでそれを次のように使用する場合

class Foo implements Trait {
}

Javaはそれを文句を言うTrait is not abstract and does not override abstract method bar() in Trait

4

2 に答える 2

135

答え

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
  • FooTraitインターフェイスを実装します
  • scalacTraitに委任することにより、メソッドを自動的に実装し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(){}};

繰り返しますが、拡張するインターフェイスTrait1Trait2なりますTraitTrait2定義するときに最後に来る場合Foo

class Foo extends Trait1 with Trait2

あなたが得るでしょう:

class Foo implements Trait1, Trait2 {
    public void bar() {
        Trait2$class.bar(this); //works because `this` implements Trait2
    }
}

ただし、切り替えTrait1Trait2Trait1最後にする)と、次のようになります。

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行にあります...

于 2011-10-03T16:32:58.280 に答える
2

barUnit(一種のNOP)を返すので、それは確かに抽象的ではありません。試す:

trait Trait {
  def bar: Unit
}

次にbar、を返すJava抽象メソッドになりvoidます。

于 2011-10-03T16:33:03.780 に答える