Defender methods and compatibility
Brian Goetz
brian.goetz at oracle.com
Tue Nov 23 11:31:39 PST 2010
Here are our latest thoughts on compatibility promises for various operations
on extension methods (adding a default to an existing method, adding a new
method with a default, removing a default from a method, changing a default, etc.)
Our first instinct was to make an analogy with operations on classes. Consider:
de-abs: Taking an existing abstract method and adding a body
re-abs: Converting a concrete method into an abstract one
add-meth: Adding a new concrete method
rem-meth: Removing a concrete method
mod-meth: Modify the body of a concrete method
Recall that a binary-compatible (BC) change does not prevent linkage with
pre-existing binaries, while a source-compatible (SC) changes does not prevent
compilation with pre-existing sources. For example, adding a method to an
interface is BC (because no pre-existing binary calls the new method), but it
is not SC (because a pre-existing class declaration that implements the
interface will now fail to compile).
With BC and SC in mind, the existing rules are:
de-abs: BC, SC
re-abs: not BC, not SC
add-meth: BC, SC*
rem-meth: not BC, not SC
mod-meth: BC, SC
*This would fail to be SC only if it created an invalid overloading, e.g.,
adding a method whose signature is incompatible with a method in a subclass,
such as adding a method "int foo()" when a subclass has a method "float foo()".
If we consider the operations on interfaces:
add-def: Add a default to an existing non-extension method
rem-def: Remove a default from an extension method
add-extn: Add a new method with a default
rem-extn: Remove a method with a default
mod-extn: Modify the default on an extension method
Ideally, these should be analogous to the existing operations:
add-def is-like de-abs (and therefore hopefully BC/SC)
rem-def is-like re-abs (no compatibility promise)
add-extn is-like add-meth (and therefore hopefully BC/SC)
rem-extn is-like rem-meth (no compatibility promise)
mod-extn is-like mod-meth (and therefore hopefully BC/SC)
These analogies only hold true to a point -- when the modifications create no
invalid multiple inheritance (i.e., two unrelated interfaces provide the same
name and signature but different defaults.) This is the multiple-inheritance
analogue of the asterisk next to SC for add-meth (creating an invalid
overloading). Whereas this is only an issue with add-meth in the existing
cases, it also becomes an issue for add-def and mod-extn with extension methods.
Here's an example of a change that is not source-compatible:
interface A { extension void m() default X.a }
interface B { }
class C implements A, B { }
// Now change and recompile B only as
interface B { extension void m() default X.b }
The change to B is not source compatible because C can no longer be
recompiled. Also, it is not binary compatible because a pre-existing binary
that calls C.m() is now ambiguous. So while we hoped that add-extn would be
BC and SC, it is neither. (The same goes for add-def and mod-extn, since the
example can be expressed in terms of adding/modifying defaults.)
However, we can arrange things so that the change to B _becomes_ binary
compatible. Observe that if a program can _ever_ be compiled, then it can
_always_ be made to link. If we record the initial "known good" configuration
of A+B+C in C's class file, then binaries that link against A+B+C can be made
to see no ambiguity even after the new B is introduced, rendering the change
to B binary compatible. Only an attempt to recompile C will give an error,
due to the fundamental source incompatibility introduced by the new B.
The mechanism to record and use a "known good" configuration will be explained
separately. The key point is that the familiar guarantee of binary
compatibility continues to be available, even with strange new multiple
inheritances due to separate compilation of extension methods.
So we have the desired behavior:
add-def - BC, SC**
rem-def - no compatibility promise
add-extn - BC, SC**
rem-extn - no compatibility promise
mod-extn - BC, SC**
Where the SC** means "providing that the modification does not create the
situation where a class multiply inherits methods with the same signature but
different defaults."
While this restriction may be unsatisfying, this is really not all that
different from the existing SC* behavior of add-meth.
More information about the lambda-dev
mailing list