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