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