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) interfaceFoo
and a (JVM) classFoo$class
with all the method implementations defined as static methods. The corresponding java code would look like something like this (for your new defintion ofFoo
):When you mix
Foo
into a concrete classBar
, what happens at the JVM level is thatBar
extends the interfaceFoo
, and it implements methodverifyConsistency
by simply forwarding the call toFoo$class
: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 forverifyConsistency
.Hence the binary incompatibility.