13

デフォルト実装のメソッドをトレイトに追加する際の下位互換性について混乱しています。お気に入り:

前のバージョン

trait Foo

新しいバージョン

trait Foo {
  def verifyConsistency: Option[String] = ??? // provide default implementation
}

Migration Managerは、この追加をバイナリ非互換性として報告します。あれは正しいですか?

4

1 に答える 1

19

そうですね、それは正しいです。

trait を定義すると、内部で (JVM) インターフェースと (JVM) クラスのFoo両方が作成され、すべてのメソッド実装が静的メソッドとして定義されます。対応する Java コードは次のようになります ( の新しい定義の場合):FooFoo$classFoo

interface Foo {
  Option<String> verifyConsistency();
}

class Foo$class {
  static Option<String> verifyConsistency(Foo self) {
    Predef.???();
  }
}

Foo具体的なクラスに混在するBarと、JVM レベルでBarインターフェイスが拡張され、次のように呼び出しを転送するだけFooでメソッドが実装されます。verifyConsistencyFoo$class

class Bar implements Foo {
  Option<String> verifyConsistency() {
    return Foo$class.verifyConsistency(this); // simple forwarding
  }
}

このようにする理由は、JVM オブジェクト モデルが多重継承をサポートしていないためです。JVM では 1 つのクラスしか拡張できないため、特性の実装を単純に拡張元のクラスに入れることはできません。

この状況の要点は、具象クラスが特性を混合するたびに、クラスが特性の各メンバーの「スタブ」メソッドを定義することです (これらのメソッドは、静的メソッドである実際の実装に単純に転送されます)。

結果の 1 つは、新しいメソッドをトレイトに追加する場合、実装を定義しても十分ではないということです: トレイトを混合する具体的なクラスを再コンパイルする必要があります (新しいメソッドのスタブがクラスに追加されるように)。 . これらのクラスを再コンパイルしないと、プログラムは実行に失敗します。これは、おそらく具体的 (非抽象的) であり、対応するインターフェイスを拡張するが、実際には新しいメソッドの実装を見逃すクラスがあるためです。

あなたの場合、これは、インターフェースを拡張するFooが、の実装を持たない具象クラスを持つことを意味しverifyConsistencyます。

したがって、バイナリの非互換性。

于 2013-08-21T23:53:06.617 に答える