Добавляет ли метод черты с реализацией нарушение обратной совместимости?
Я запутался в отношении обратной совместимости при добавлении метода с реализацией по умолчанию для черты. Подобно:
Предыдущая версия
trait Foo
Новая версия
trait Foo {
def verifyConsistency: Option[String] = ??? // provide default implementation
}
Менеджер миграции сообщает об этом добавлении как о двоичной несовместимости. Это верно?
1 ответ
Ну да это правильно.
Когда вы определяете черту Foo
Под капотом создаст оба (JVM) интерфейса Foo
и (JVM) класс Foo$class
со всеми реализациями метода, определенными как статические методы. Соответствующий код Java будет выглядеть примерно так (для вашего нового определения 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.
Ситуация заключается в том, что каждый раз, когда конкретный класс смешивает черту, класс определяет методы-заглушки для каждого члена черты (эти методы просто передают фактическую реализацию, которая является статическим методом).
Одним из следствий этого является то, что если вы добавляете новый метод в признак, даже если вы определяете реализацию, этого недостаточно: необходимо перекомпилировать конкретные классы, которые смешивают признак (так что заглушка для нового метода добавляется в класс), Если вы не перекомпилируете эти классы, ваша программа не сможет работать, так как теперь у вас будет класс, который предположительно является конкретным (не абстрактным) И расширяет соответствующий интерфейс, но фактически пропускает реализацию нового метода.
В вашем случае это означает наличие конкретных классов, расширяющих интерфейс Foo
но не имеют никакой реализации для verifyConsistency
,
Отсюда двоичная несовместимость.