Formal model for defender method resolution

Peter Levart peter.levart at marand.si
Wed Feb 2 01:33:20 PST 2011


On 02/02/11, Peter Levart wrote:
> 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.

Ok, you may say that by stating "void m() default none", C says that any semantics provided by overriden methods should be ignored, even if it comes from explicitly mixed-in paths. But should we grant C such power? Isn't that D's concern? D can choose to mix-in the semantics of A.m (by extending A explicitly although it already extends C which extends A) or to ignore any inherited semantics (by overriding and stating "default none").

> 
> 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