13

在将具有默认实现的方法添加到特征时,我对向后兼容性感到困惑。喜欢:

以前的版本

trait Foo

新版本

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

迁移管理器将此添加报告为二进制不兼容。那是对的吗?

4

1 回答 1

19

嗯,是的,它是正确的。

当您定义 traitFoo时,它将在后台创建一个(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 上扩展单个类。

这种情况的好处是,每次具体类混合一个特征时,该类都会为特征的每个成员定义“存根”方法(这些方法只是转发到实际实现,这是一个静态方法)。

一个后果是,如果您向 trait 添加一个新方法,即使您定义了一个实现也不够:需要重新编译混合该 trait 的具体类(以便将新方法的存根添加到类中) . 如果您不重新编译这些类,您的程序将无法运行,因为您现在将拥有一个假定为具体(非抽象)并扩展相应接口但实际上错过了新方法的实现的类。

在您的情况下,这意味着拥有扩展接口Foo但没有任何实现的具体类verifyConsistency

因此二进制不兼容。

于 2013-08-21T23:53:06.617 回答