JDK-6487370 : javac incorrectly gives ambiguity warning with override-equivalent abstract inherited methods
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 5.0,7
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: generic,solaris_8
  • CPU: generic
  • Submitted: 2006-10-27
  • Updated: 2011-05-18
  • Resolved: 2011-05-18
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 7
7 b40Fixed
Related Reports
Duplicate :  
Relates :  
Relates :  
Relates :  
Description
An Eclipse developer observed that javac detects an error in the program below, but the Eclipse compiler does not.

interface J {
  String foo(Number n);
}
interface K {
  Object foo(Number n);
}
public abstract class X implements J, K {
  void foo() {
    foo(0.0f); // javac error: ambiguous
  }
}
class Z extends X {
  public String foo(Number f) {
      return null;
  }
  public static void main(String args[]) {
      System.out.println(new Z().foo(0.0f));
  }
}

Comments
SUGGESTED FIX A webrev of this fix is available at the following URL http://hg.openjdk.java.net/jdk7/tl/langtools/rev/db77bf6adb53
02-07-2008

EVALUATION The problem is due to a fix, specifically the one for 4907941. In that fix overload resolution has been slightly changed wrt to the JLS (actually I have to recognize that the fix has been done prior the JLS!). In particular, if two most-specific overriding-equivalent methods are found m1 and m2, the compiler checks that those methods don't have the same erasure. However, this check should concern only about the signature of the two methods regardless of their return types. Instead, because of a bug, javac consider also the return type in the equation so that, in the submitted case we have that: |m1| = String foo(Number) |m2| = Object foo(Number) since |m1| != |m2| the compiler raise the ambiguity error. Instead, the check should have been: |arguments(m1)| = Number |arguments(m2)| = Number since |arguments(m1)| = |arguments(m2)| (that is the erasure of the arguments of the two applicable methods is the same), javac should go on and pick up the method that has the most specific return type (m1 in this case).
02-07-2008

EVALUATION > interface J { > String foo(Number n); > } > interface K { > Object foo(Number n); > } - J::foo(Number) and K::foo(Number) are override-equivalent (8.4.2). > public abstract class X implements J, K { > void foo() { > foo(0.0f); // javac error: ambiguous > } > } - X has no method that overrides or hides foo in J or K. Therefore, X inherits 'abstract String foo(Number)' and 'abstract Object foo(Number)' as well as having its own 'void foo()'. - By 8.4.8.4: "If all the inherited members are abstract, then the class is necessarily an abstract class and is considered to inherit all the abstract methods. A compile-time error occurs if, for any two such inherited methods, one of the methods is not return type substitutable forthe other." As 'abstract String foo(Number)' and 'abstract Object foo(Number)' ARE return-type substitutable, there is no compile-time error for 'void foo()' itself. So we have: public abstract class X implements J, K { abstract String foo(Number n) {} abstract Object foo(Number n) {} void foo() { foo(0.0f); } } - This is OK re: JLS page 177, since String and Object are compatible return types. The example on page 188/189 concerns return types that are clearly not compatible. - Page 177 ought to say "This situation can occur if the class would have as members two abstract methods tat have the same method signature (8.4.2) but return types which are not return-type substitutable (8.4.5)." The call to foo(0.0f): - No matching methods are applicable by subtyping (15.12.2.2). - Both abstract methods in X are then applicable by method invocation conversion (15.12.2.3). Method invocation conversion allows a float to undergo a widening primitive conversion from float to double; no method is applicable. But a float can also undergo a boxing conversion to Float and then a widening reference conversion to Number. - We proceed to choose the most specific method, which does not consider the return type, so both abstract methods are maximally specific. Because they are override-equivalent, and abstract, "the most specific method is chosen arbitrarily among the subset of the maximally specific methods that have the most specific return type." - Therefore, the compiler ought to pick 'abstract String foo(Number)'. > class Z extends X { > public String foo(Number f) { > return null; > } > public static void main(String args[]) { > System.out.println(new Z().foo(0.0f)); > } > } To complete the picture: - Z's declaration of 'public String foo(Number)' overrides both abstract foo's from X (8.4.8.1). - Z's invocation of foo(0.0f) correctly chooses 'String foo(Number)'. - To declare 'Object foo(Number)' in Z would be an error.
27-10-2006