デフォルト実装のメソッドをトレイトに追加する際の下位互換性について混乱しています。お気に入り:
前のバージョン
trait Foo
新しいバージョン
trait Foo {
def verifyConsistency: Option[String] = ??? // provide default implementation
}
Migration Managerは、この追加をバイナリ非互換性として報告します。あれは正しいですか?
デフォルト実装のメソッドをトレイトに追加する際の下位互換性について混乱しています。お気に入り:
前のバージョン
trait Foo
新しいバージョン
trait Foo {
def verifyConsistency: Option[String] = ??? // provide default implementation
}
Migration Managerは、この追加をバイナリ非互換性として報告します。あれは正しいですか?
そうですね、それは正しいです。
trait を定義すると、内部で (JVM) インターフェースと (JVM) クラスのFoo
両方が作成され、すべてのメソッド実装が静的メソッドとして定義されます。対応する Java コードは次のようになります ( の新しい定義の場合):Foo
Foo$class
Foo
interface Foo {
Option<String> verifyConsistency();
}
class Foo$class {
static Option<String> verifyConsistency(Foo self) {
Predef.???();
}
}
Foo
具体的なクラスに混在するBar
と、JVM レベルでBar
インターフェイスが拡張され、次のように呼び出しを転送するだけFoo
でメソッドが実装されます。verifyConsistency
Foo$class
class Bar implements Foo {
Option<String> verifyConsistency() {
return Foo$class.verifyConsistency(this); // simple forwarding
}
}
このようにする理由は、JVM オブジェクト モデルが多重継承をサポートしていないためです。JVM では 1 つのクラスしか拡張できないため、特性の実装を単純に拡張元のクラスに入れることはできません。
この状況の要点は、具象クラスが特性を混合するたびに、クラスが特性の各メンバーの「スタブ」メソッドを定義することです (これらのメソッドは、静的メソッドである実際の実装に単純に転送されます)。
結果の 1 つは、新しいメソッドをトレイトに追加する場合、実装を定義しても十分ではないということです: トレイトを混合する具体的なクラスを再コンパイルする必要があります (新しいメソッドのスタブがクラスに追加されるように)。 . これらのクラスを再コンパイルしないと、プログラムは実行に失敗します。これは、おそらく具体的 (非抽象的) であり、対応するインターフェイスを拡張するが、実際には新しいメソッドの実装を見逃すクラスがあるためです。
あなたの場合、これは、インターフェースを拡張するFoo
が、の実装を持たない具象クラスを持つことを意味しverifyConsistency
ます。
したがって、バイナリの非互換性。