Formal model for defender method resolution

David Holmes David.Holmes at oracle.com
Mon Jan 31 20:31:55 PST 2011


David M. Lloyd said the following on 02/01/11 12:31:
> Honestly I don't think of this as re-abstraction.  It's the *default* 
> implementation, not *the* implementation.  You're saying, "if nobody 
> else in the hierarchy says otherwise, do this".  Especially if the 
> syntax is different.

But if you can't re-asbtract the method so that there is no default, 
subtypes can inherit an unsound default implementation. In that case the 
pattern being forced here is to always add a default even if it is:

   void m() default Util.throwNoSuchMethodError("implement me correctly")

It also means that adding a defender to a super interface exposes this 
potential danger to existing subtypes. For example if my framework uses 
a core interface Foo

   interface Foo {
     void m();
   }

and extends it:

   interface MyFoo extends Foo {
      /** refined semantics for m() */
      void m();
     void n();
     void o();
   }

and then has an abstract base class

   abstract class AbstractBase {
      public void n() { ... }
      public void o() { ... }
      // subclasses need to get m() right themselves
    }

then any subclass of AbstractBase is required to implement m() or remain 
abstract. But if a defender is added to Foo.m these AbstractBase 
subclasses magically get a default implementation that is incorrect! I 
don't know about you but I'd want this flagged as an error at build time.

David Holmes

> Anyway, if someone has re-overridden a defender method it probably means 
> they did so *before* the defender method existed, and they probably 
> already have an implementation for it so it doesn't matter.  If they 
> override a defender method after the fact, they'd probably change the 
> default if they wanted to.
> 
> That said it would be nice if one could *explicitly* remove the default 
> implementation by overriding it.  Maybe "default abstract" or something.
> 
> On 01/31/2011 08:19 PM, Brian Goetz wrote:
>> All things being equal, I would agree with (1) and (3).  Unfortunately
>> reabstraction of defended methods seems to complicate resolution, and
>> given that people use reabstraction fairly infrequently now, it may be a
>> better choice to accept some additional apparent inconsistency in
>> exchange for simpler semantics.
>>
>> I am not sure I agree with (2), because it is not uncommon to see an
>> overriding purely to add a note to the Javadoc, enough so that
>> supporting reabstraction "breaks" the common intuition that
>> (non-covariantly) overriding a method in an interface is a no-op.
>>
>>
>>
>> On 1/31/2011 8:56 PM, Howard Lovatt wrote:
>>> Hi,
>>>
>>> I think it is best that a defended method can be re-abstracted (like
>>> abstract classes currently do) for three reasons:
>>>
>>> 1. It is the expected behaviour. If you see "m();" in an interface,
>>> you expect it to be abstract and you expect to have to implement it.
>>> You should not be required to check the hierarchy to see if it is
>>> inheriting something unexpected. If you forget to implement the method
>>> you won't be warned, silently and potentially insidiously a default
>>> will appear.
>>>
>>> 2. It is the only safe assumption. If someone is overriding a defended
>>> method then they are doing so for some good reason. Commonly, the
>>> semantics of the method are subtly altered and a modification to the
>>> Javadoc is required to note the change. The fact that the semantics
>>> have changed means that inheriting the method isn't safe and therefore
>>> it should be re-abstracted.
>>>
>>> 3. It is important to be consistant with abstract classes, since
>>> defender methods are similar to abstract classes and it will be
>>> confusing if they behave differently. People may well start to use
>>> defender methods were they previously used abstract classes, therefore
>>> the two should be as interchangeable as possible.
>>>
>>> Cheers,
>>>
>>>    -- Howard.
>>>
>>> On 29 January 2011 13:03, David Holmes<David.Holmes at oracle.com>   wrote:
>>>> Alex,
>>>>
>>>>    >   Again, why do defenders if defending a method in a top superinterface
>>>>    >   is going to be undone by "accidental" overriding (by rather hopeless
>>>>    >   abstract methods) in subinterfaces?
>>>>
>>>> Let's not forget that the primary use-case for defenders is to allow us
>>>> to add _new_ methods to interfaces together with an implementation so
>>>> that (most) existing classes will continue to compile and execute
>>>> correctly. Adding defenders to existing methods is not the primary
>>>> use-case and as per past discussions is somewhat perilous.
>>>>
>>>> The crux of this matter is "accidental overriding" versus "deliberate
>>>> overriding". If I override an interface method f() in B to specialize it
>>>> compared to how it is defined in A then not only do I not want to
>>>> inherit A's defender for f(), it would be inherently incorrect to do so
>>>> because it does not implement the correct semantics.
>>>>
>>>> You seem to want to cater for the programmer who accidentally overrides
>>>> f() (to tweak javadoc in a semantically non-changing way) to still get a
>>>> defender that exists somewhere in the inheritance hierarchy. Whereas I
>>>> (and I think others) expect overriding (with no explicit defender) to
>>>> mean reabstraction of the method.
>>>>
>>>> Why are you against reabstraction? Reabstraction is always safe, if not
>>>> always convenient. Silent inheritance may be convenient for some but is
>>>> potentially unsafe.
>>>>
>>>> Why should the addition of a defender in a super interface break my
>>>> framework by allowing subclasses to silently inherit use of a defender
>>>> that doesn't implement the semantics of the current type?
>>>>
>>>> David Holmes
>>>>
>>>> Alex Buckley said the following on 01/29/11 11:06:
>>>>> On 1/28/2011 3:55 PM, Neal Gafter wrote:
>>>>>> Die due to no f impl, exactly as it does today.  In this case it is
>>>>>> irrelevant that one of the newly added methods has a default, as that
>>>>>> default could not possibly be the one that the call resolves to.  This
>>>>>> is a situation that occurs today and that is indeed what happens.
>>>>> Why bother with defenders if a call to a defended method (and I think
>>>>> C.f _is_ defended by A.f, despite B.f) is going to die at runtime?
>>>>>
>>>>> Not sure what you mean by "that default could not possibly be the one
>>>>> that the call resolves to." The caller who causes execution of
>>>>> invokeinterface B.f()B; has no idea whether the receiver class
>>>>> implements f via a defender or via declaration/class inheritance.
>>>>>
>>>>>>       If there's no error for B, and B.f _does_ inherit A.f's default, then
>>>>>>       invokeinterface B.f()B on a C may return an A that's not a B, and we
>>>>>>       definitely have unsoundness.
>>>>>>
>>>>>> Agreed!  Which is why B.f should not inherit A.f's default.
>>>>>>
>>>>>>
>>>>>>       So either there should be an error on B;
>>>>>>
>>>>>>
>>>>>> Huh?  Why?  This is a simple case of reabstraction.  There's nothing
>>>>>> wrong with B.  There might or might not be something wrong with some
>>>>>> further derived type, but B is fine.
>>>>> It's not reabstraction if A.f is given a defender after B gains f.
>>>>> Defending the highest superinterface alone seems like a common migration
>>>>> path, one we should support.
>>>>>
>>>>>>       or no error yet _B.f inherits
>>>>>>       A.f's default and the inherited defender is typechecked in its new
>>>>>>       environment_.
>>>>>>
>>>>>> Huh?  Why?  When you reabstract a method, it has no implementation to
>>>>>> inherit or check.
>>>>> Again, why do defenders if defending a method in a top superinterface is
>>>>> going to be undone by "accidental" overriding (by rather hopeless
>>>>> abstract methods) in subinterfaces?
>>>>>
>>>>> Alex
>>>>>
>>>>
>>>
>>>
> 
> 


More information about the lambda-dev mailing list