Other |
---|
tbd_majorUnresolved |
Blocks :
|
|
Duplicate :
|
|
Duplicate :
|
|
Relates :
|
|
Relates :
|
|
Relates :
|
|
Relates :
|
|
Relates :
|
|
Relates :
|
|
Relates :
|
This spec bug arose out of an investigation of compiler bug 7111664 (see also 6946211). At issue is the definition of members of an intersection (and, thus, a type variable) (4.9). If you squint, you might see an implicit assertion, sometimes enforced by the compiler, that the imaginary class declaration we use to model membership must be well-formed. If inherited methods clash in certain ways, for example, an error occurs. (However, some checks, such as the requirement that a subclass make an explicit call to super() if the superclass doesn't have a no-arg constructor, are not enforced.) If it is our intent that the imaginary class be strictly well-formed, the spec needs to be clear about it. If not, the spec needs to outline exactly what assertions must be made about the imaginary class (or abandon the imaginary class crutch and just describe the assertions directly). In my opinion, the rule should be much more forgiving than those for class declarations: unless we can prove that no witness can exist for an intersection, we should assume it does exist, and not complain. This may mean we have fewer guarantees about membership than we thought, for things like choosing the return type of a method invocation (15.12.2.5). But these guarantees, like an assumption that there always exists a most-specific return type, are illusory anyway: parameterized types don't have the guarantees that simple classes do, and we can't enforce the guarantees in non-Java class files. (I ran into this when trying to reason about functional interface members.) To illustrate: class TvarAccess { /*** Declarations ***/ static abstract class A { protected abstract String foo(); protected String bar() { return "bar"; } protected abstract Object clone(); } interface Foo { public String foo(); } interface Bar { public String bar(); } interface PublicClone { public Object clone(); } // Witness for A&Foo, A&Bar, A&PublicClone, Object&PublicClone static class C extends A implements Foo, Bar, PublicClone { public String foo() { return "foo"; } public String bar() { return "bar"; } public Object clone() { return this; } } interface ReadableFactory { Readable make(); } interface CloseableFactory { java.io.Closeable make(); } // Witness for ReadableFactory&CloseableFactory interface ReaderFactory extends ReadableFactory, CloseableFactory { java.io.Reader make(); } class RequiresSuperCall { public RequiresSuperCall(boolean arg) {} } // Witness for T extends RequiresSuperCall class D extends RequiresSuperCall { public D() { super(true); } } /*** Tests ***/ // No error for tvar in javac 7 public <T extends A & Foo> String testMethod1(T arg) { return arg.foo(); } // ERROR for tvar in javac 7 public <T extends A & Bar> String testMethod2(T arg) { return arg.foo(); } // ERROR for tvar in javac 7 public <T extends A & PublicClone> void testMethod3(T arg) { arg.clone(); } // ERROR for tvar in javac 7 public <T extends Object & PublicClone> void testMethod4(T arg) { // error arg.clone(); } // ERROR for tvar in javac 7 public <T extends ReadableFactory & CloseableFactory> void testMethod5(T arg) { arg.make(); // ERROR in javac 7: ambiguous method } // No error for tvar in javac 7 public <T extends RequiresSuperCall> void testMethod6(T arg) { } }
|