Добавляет ли метод черты с реализацией нарушение обратной совместимости?

Я запутался в отношении обратной совместимости при добавлении метода с реализацией по умолчанию для черты. Подобно:

Предыдущая версия

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,

Отсюда двоичная несовместимость.

Другие вопросы по тегам