Re-specification and re-abstraction

Brian Goetz brian.goetz at oracle.com
Thu Dec 22 14:14:50 PST 2011


>> The tension here is between respecting the existing language behavior,
>> intended or not -- where exact redeclarations of interface methods is a
>> no-op -- and how we want this "new" feature to work, and who loses when
>> existing usage patterns conflict with the desired usage patterns for the
>> new feature.
>
> The no-op-ness of "exact redeclaration of interface method" is respected
> either way if the definition of "exact" is adequate.

Yes, if you exactly redeclare the default too, it is also a no-op.  So, 
more precise statement: "exact redeclaration of the return type, name, 
and signature, but no default clause."

>> The existing semantics were designed to respect the no-op-ness of these
>> redeclarations, effectively making them "invisible" to the default
>> calculation, rather than give a potentially surprising new meaning to a
>> hitherto meaningless (but used in practice) construct.
>
> So the scenario that current semantics is trying to serve best is the
> following:
>
> // author X creates:
>
> interface A {
>    void m() defaut { ... }
> }
>
> // then comes user Y and extends A:
>
> interface AExt extends A {
>    /**
>     * Just Some JavaDoc...
>     */
>    @Override void m();
>
>     ...
> }
>
>
> Now, you are saying that user Y might expect that method redeclaration is a
> no-op and would be surprised if it was in fact re-abstraction.

That is right.

> Maybe so, but
> he would certainly be the 1st to implement the AExt interface and find that
> out. There is practicaly no chance that he would not notice that and would
> therefore deprive other implementors of AExt from the chance of utilizing the
> A's default.

I am afraid this involves some wishful thinking about how quickly 
maintainers of libraries will respond to complaints from their 
customers!  The problem comes in when extension crosses maintenance 
domains, and you cannot just go "fix" the erroneous redeclaration.

> Or are you planning to replace existing methods in existing interfaces with
> default methods as an alternative to extending abstract base classes. For
> example:
>
> Instead of extending from AbstractList, one could just implement List and get
> implementations of methods that are now part of AbstractList via default
> methods in List interface.

I expect this to be a relatively frequent occurrence; where you might 
have used an abstract class before, if that abstract class has no state, 
you can now instead put the behavior in the interface, and not "burn" 
the single superclass of your subtypes.  Abstract classes will still be 
needed when they have state or interesting constructor behavior.

> In that case maybe one would want those defaults to be inherited by pre-
> existing extensions of List that "just redeclare" those methods.

Yes, this is one case where the pass-through behavior is desirable.

As I said, both directions are somewhat unfortunate.

>
> Peter
>
>>
>> Neither answer is obviously right, or even good (and anyone claiming
>> otherwise has not thought through it enough.)  Its only a matter of
>> which alternative is less bad, and there's arguments on both sides.  We
>> went back and forth on this one a few times already, and may well go
>> back and forth on it a few times more before we're done.
>>
>> On 12/22/2011 2:25 AM, Peter Levart wrote:
>>> On 12/22/11, David Holmes wrote:
>>>> One of the things that concerns me in the current State of the Lambda
>>>> is
>>>> the fact that re-specification of method on a sub-interface does not
>>>> re-abstract the method, but continues to allow any default from a
>>>> super
>>>> interface to be applied to that method. This is inherently wrong in my
>>>> opinion.
>>>>
>>>> Consider this fictitious but plausible example.
>>>>
>>>> We have the present Map interface. We also have a ConcurrentMap
>>>> interface that adds some additional methods that provide atomicity for
>>>> compound actions on maps that support concurrent access.
>>>>
>>>> Suppose that ConcurrentMap actually re-specified each of the Map
>>>> methods
>>>> to add various thread-safety and atomicity guarantees.
>>>>
>>>> Now suppose that the maintainer of Map decides to take advantage of
>>>> default methods and adds default implementations to some of those Map
>>>> methods. This is not done with ConcurrentMap in mind and those
>>>> implementations offer no specific thread-safety or atomicity
>>>> guarantees.
>>>> It should be obvious that those implementations are not likely to be
>>>> valid for any class that implements ConcurrentMap.
>>>>
>>>> Given the current semantics however, if I were to write a class that
>>>> implements ConcurrentMap and accidentally forgot to implement those
>>>> methods for which a default exists in Map, then my class will in fact
>>>> "inherit" those implementations. And in doing so my class would be
>>>> completely broken, but I would be unaware of it.
>>>>
>>>> To me this is completely wrong - we should never sacrifice
>>>> correctness.
>>>> A supertype does not in general know about any subtypes and so a
>>>> supertype's default implementation should not apply if the subtype has
>>>> redefined that method. Any change to the "documentation" of a method
>>>> is
>>>> a change to its specification. Only the author of that change is in a
>>>> position to say whether a default implementation in a supertype is
>>>> still
>>>> applicable - so to assume that it is by default (no pun intended) is
>>>> just wrong in my view.
>>>>
>>>> If redefinition of a method always re-abstracted it then, I think, no
>>>> explicit syntax for re-abstraction would be needed.
>>>>
>>>> David Holmes
>>>
>>> I aggree. Only the author of the interface should say when an if the
>>> "new" default on a superinterface is adequate. I don't see any drawback
>>> when re-specification of a method in a subinterface has the semantics
>>> of re-abstraction compared to current state where the default is
>>> inherited. It certainly doesn't break any code.
>>>
>>> Looking from the perspective of code evolution: if an interface method
>>> was abstract (non-default) at the start than any non-abstract
>>> implementations of this interface or subinterface already specifies a
>>> concrete implementation for that method. Changing this abstract method
>>> to a default method on a superinterface does not break anything even if
>>> this method is re-abstracted on any subinterface (because class allways
>>> wins).>
>>>    Regards, Peter


More information about the lambda-dev mailing list