I am confused regarding backward compatibility when adding a method with default implementation to a trait. Like:
Previous Version
trait Foo
New Version
trait Foo {
def verifyConsistency: Option[String] = ??? // provide default implementation
}
The Migration Manager reports this addition as a binary incompatibility. Is that correct?
Well yes it is correct.
When you define trait Foo
, it will under the hood create both a (JVM) interface Foo
and a (JVM) class Foo$class
with all the method implementations defined as static methods. The corresponding java code would look like something like this (for your new defintion of Foo
):
interface Foo {
Option<String> verifyConsistency();
}
class Foo$class {
static Option<String> verifyConsistency(Foo self) {
Predef.???();
}
}
When you mix Foo
into a concrete class Bar
, what happens at the JVM level is that Bar
extends the interface Foo
, and it implements method verifyConsistency
by simply forwarding the call to Foo$class
:
class Bar implements Foo {
Option<String> verifyConsistency() {
return Foo$class.verifyConsistency(this); // simple forwarding
}
}
The reason why it is done this way is that the JVM object model does not support multiple inheritance. The traits implementations cannot be simply put in classes that you would extend from, because you can only ever extend a single class on the JVM.
The take away of this situation is that everytime a concrete class mixes a trait, the class defines "stub" methods for each member of the trait (those methods simply forward to the actual implementation, which is a static method).
One consequence is that if you add a new method to a trait, even if you define an implementation it is not enough: concrete classes that mix the trait need to be recompiled (so that a stub for the new method is added to the class). If you don't recompile those classes, your program will fail to run, as you would now have a class that is supposedly concrete (non abstract) AND extend the corresponding interface but actually miss the implementation for the new method.
In your case this means having concrete classes that extend interface Foo
but do not have any implementation for verifyConsistency
.
Hence the binary incompatibility.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With