Formal model for defender method resolution
Peter Levart
peter.levart at marand.si
Wed Feb 2 00:57:22 PST 2011
On 02/02/11, David Holmes wrote:
> Neal Gafter said the following on 02/02/11 16:30:
> > So far so good. But none of these examples prevent *any *attempt to inherit
> > from the interface, they only prevent inheriting from the interface without
> > overriding the method family in question. That's no different from any
> > other interface method today, except for the fact that the intent of these
> > new defender methods is that they not demand to be implemented directly by
> > classes that implement them.
> >
> > For the specific case of
> >
> > intf A { String m() default X.a }
> > intf B { String m() default X.b }
> > intf C extends A { String m() default none }
> > intf D extends A, B, C { }
> >
> > A class that implements D has a single, unique, non-overridden
> > implementation for the method family (B.m), and it is a suitable
> > implementation (i.e. the return type works) for the inherited abstract
> > method. So there doesn't appear to be any problem with that class, and
> > therefore there should be no problem with these interfaces
Neal, what happens to this interface:
inf F extends A, C {}
Do implementations of F have to provide an implementation for m()? Do you consider that if a method is overriden in C then it doesn't matter if it is also mixed-in explicitly? Does anything in existing Java method resolution rules express that a "redundant" interface inheritance graph should be pruned in any way?
I would suggest a slightly different semantics for the construct that is used in intf C.
A "default none" could be treated as a flag that says "stop searching for the default definition in super-interfaces at this point", but continue recursive search through the rest of inheritance hraph.
This would effectively transform the above interface into:
intf C /* extends A */ { String m() /* default none */ }
... as far as defaults are concerned (ignoring the fact that C now isn't a subtype of A and that C.m doesn't override A.m).
So using this semantics, the definition of D:
intf D extends A, B, C { }
... can be flagged as problematic because of conflicting defaults comming from A.m and B.m. (as far as compiler diagnostics is concerned, this should be a warning - not an error, since such interface isn't unusable)
C.m has no default and therefore does not cause any problem in the following example:
intf E extends B, C {}
Implementations of E would get a default from B.m.
But the implementations of:
inf F extends A, C {}
would get a default from A.m.
I think that such semantics provides better natural separation of concerns.
I don't know whether it causes any unsoundness with current Java method resolution semantics though.
>
> I have to disagree with you there. The semantics for A.m, B.m and C.m
> are potentially different, so what semantics does D.m provide ?
I would say (considering my suggested semantics of "default none") that C.m does not provide any semantics (it just forces the implementor or a mixer to provide one). So "D" here is "a mixer" that mixes in A.m and B.m defaults, which are, unfortunately, conflicting.
Is anything wrong with this reasoning?
Regards, Peter
> We have
> no idea looking at what has been written. Of course this can happen
> today without defenders, but we assume that an implementor of D can
> somehow make sense of this strange situation. But with defenders in play
> it doesn't make sense to me for this situation to be ignored by the
> compiler because the compiler can see there are two conflicting
> defenders at play here: X.b from B and <none> from C. From D's
> perspective B and C have equal weighting, so it is ambiguous what
> defender D.m should have. Hence the programmer should be forced to
> explicitly resolve that ambiguity in my view. Why do you consider B's
> defender to be more relevant to D than C's?
>
> > The same situation can occur with classes today:
> >
> > *class A<T> {
> > public T m() { ... }
> > public abstract String m();
> > }
> > class D extends A<String> {
> > }
> > *
> >
> > In this case the inherited non-abstract m implements the inherited abstract
> > m. It seems that we should allow the same thing for interfaces: a
> > (non-overridden) inherited concrete method should be capable of implementing
> > an inherited abstract method.
>
> Is this is an example we should strive to emulate? It seems an extreme
> quirk of generics. Is there a realistic use-case for this?
>
> David Holmes
>
>
More information about the lambda-dev
mailing list