Formal model for defender method resolution
Howard Lovatt
howard.lovatt at gmail.com
Mon Jan 31 20:53:43 PST 2011
David,
Comments in line
On 1 February 2011 13:31, David M. Lloyd <david.lloyd at redhat.com> wrote:
> 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.
I think the same about *any* method body, unless final, it is just the
*default* that I am inheriting not *the* implementation. Hence the
argument for the same behaviour as any other class hierarchy.
This also raises the question of if final should be allowed for a
method with a default?
> 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.
These arguments are neutral for if the method is re-abstracted or not.
However another scenario, which I think will be common, is the default
is added to an existing method of a library that you use. You have no
way of anticipating this and it will not be obvious it has happened
(without trawling the Javadoc for all the methods in a library - who
does that for each release!).
Lets make this a concrete example, in List there is an add method and
out of the goodness of their heart Oracle add a default to it
(AbstractList already has one). Now in my version of List,
NonNullList, I inherit List but override add noting that you can't add
nulls to this list. I now write a new implementation of NonNullList
and mean to override add but I forget. As things stand now the
compiler tells me that I haven't overridden add. However if the
default comes through past my override, then the compiler no longer
warns me and I have a difficult to find bug.
I think this type of bug may well happen since people won't track
third party API's, even java.util ones, that closely and may be
completely unaware that they now have a default.
> That said it would be nice if one could *explicitly* remove the default
> implementation by overriding it. Maybe "default abstract" or something.
How about the other way round:
Object m(); // means re-abstract (assuming it has a default, if it
doesn't then it is still abstract - no error)
Object m() default super; // means inherit the super method even
though I have redefined the method (error if no super default or more
than one super default exists)
Object m() default A.super; // means explicitly inherit the default
from super interface A (error if A isn't a super interface or if m
doesn't have a default in A)
-- Howard.
> 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
>>>>>
>>>>
>>>>
>>>
>>>
>>>
>>
>
>
> --
> - DML
More information about the lambda-dev
mailing list