Formal model for defender method resolution

Dan Smith daniel.smith at oracle.com
Fri Feb 18 14:52:04 PST 2011


On Feb 2, 2011, at 9:56 PM, Neal Gafter wrote:

> On Wed, Feb 2, 2011 at 12:57 AM, Peter Levart <peter.levart at marand.si>wrote:
> 
>> On 02/02/11, David Holmes wrote:
>>> Neal Gafter said the following on 02/02/11 16:30:
>>>> 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
>> 
>> Does anything in existing Java method resolution rules express that a
>> "redundant" interface inheritance graph should be pruned in any way?
>> 
> 
> How would you be able to tell?

import java.util.*;

public class MultipleAbstract {

  interface I1 { List<String> foo(); }
  interface J1 { LinkedList foo(); }
  interface K1 extends I1, J1 {}

  interface I2 { List<String> foo(); }
  interface J2 extends I2 { LinkedList foo(); }
  interface K2 extends I2, J2 {}

  interface I3 { List<String> foo(); }
  interface J3 extends I3 { LinkedList foo(); }
  interface K3 extends J3 {}

  void m1(K1 k1, K2 k2, K3 k3) {
    LinkedList l1 = k1.foo(); // javac: ambiguous; Eclipse: ok
    LinkedList l2 = k2.foo(); // javac: ok; Eclipse: ok
    LinkedList l3 = k3.foo(); // javac: ok; Eclipse: ok
  }

}

JLS (15.12.2.5) says the "the most specific method is chosen arbitrarily among the subset of the maximally specific methods that have the most specific return type" -- it doesn't say what to do if there is no such method.  And it's not very clear about what "most specific return type" means (my best guess, based on the above definition for methods, is to use subtyping; but probably the right thing to do is to infer a definition from "return-type-substitutable" (8.4.5)).

On inheritance, the JLS (8.4.8) says: "A class C inherits from its direct superclass and direct superinterfaces all [accessible] methods [that] are neither overridden (8.4.8.1) nor hidden (8.4.8.2) by a declaration in the class."  Methods inherited from other direct superinterfaces do not qualify as "a declaration in the class."

I also tried this:

  interface L1 { void foo() throws Throwable; }
  interface M1 { void foo() throws ClassNotFoundException; }
  interface N1 extends L1, M1 {}

  interface L2 { void foo() throws Exception; }
  interface M2 extends L2 { void foo() throws ClassNotFoundException; }
  interface N2 extends L2, M2 {}

  interface L3 { void foo() throws Exception; }
  interface M3 extends L3 { void foo() throws ClassNotFoundException; }
  interface N3 extends M3 {}

  void m2(N1 n1, N2 n2, N3 n3) {
    n1.foo(); // javac: CNFE; Eclipse: CNFE
    n2.foo(); // javac: CNFE; Eclipse: CNFE
    n3.foo(); // javac: CNFE; Eclipse: CNFE
  }

Here, the JLS is clear (15.12.2.5) that N1.foo() is supposed to throw nothing: "the most specific method is considered to throw a checked exception if and only if that exception or its erasure is declared in the throws clauses of each of the maximally specific methods".  But the compilers have done their own (more reasonable) thing.

—Dan



More information about the lambda-dev mailing list