web-dev-qa-db-ja.com

Scalaトレイトを実装したメソッドの特性をJava

ScalaトレイトでJavaから実装されたメソッドを呼び出すことは不可能だと思いますか、または方法はありますか?

私がScalaにいるとします:

_trait Trait {
  def bar = {}
}
_

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

_class Foo implements Trait {
}
_

JavaはTrait is not abstract and does not override abstract method bar() in Trait

59
Jus12

回答

からJavaパースペクティブ_Trait.scala_はTraitinterfaceにコンパイルされます。したがって、JavaでTraitを実装すると、インターフェースを実装すると解釈されます-これにより、エラーメッセージがわかりやすくなります。短い回答:Javaの特性実装を利用することはできません。これは、Java(!)

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_と混同しないでください)クラスは透過的に作成され、技術的にはnotTraitインターフェースを実装します。ただし、Traitインスタンスを引数として取るstatic bar()メソッドがあります(thisの一種)
  • FooTraitインターフェースを実装します
  • scalacは、_Trait$class_に委任することにより、Traitメソッドを自動的に実装します。これは基本的に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を拡張するインターフェースになります。ここで、Fooを定義するときに_Trait2_が最後に来る場合:

_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"
}
_

ここにステロイドの新しい「フー」があります:

_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 $$ super $ bar()」メソッド呼び出しと文字列「、Trait2」の連結を返す、Trait2 $ classの「bar」静的メソッド。
  • 呼び出すFooOnSteroidsインスタンスの 'Trait2 $$ super $ bar()' ...
  • これを引数として渡し、「Trait1 $$ super $ bar()」メソッド呼び出しと文字列「、Trait1」の連結を返す、Trait1 $ classの「bar」静的メソッド。
  • 呼び出すFooOnSteroidsインスタンスの 'Trait1 $$ super $ bar' ...
  • オリジナルのFooの 'bar'メソッド

そして結果は「Foo、Trait1、Trait2」です。

結論

すべてを読み終えた場合、元の質問に対する答えは最初の4行にあります...

130

barが空のUnit(NOPの一種)を返しているため、実際には抽象的ではありません。試してください:

trait Trait {
  def bar: Unit
}

その場合、barは、Java voidを返す抽象メソッドになります。

2
paradigmatic