JDK-6987475 : Order of declarations affects whether abstract method considered overridden
  • Type: Bug
  • Component: tools
  • Sub-Component: javac
  • Affected Version: 6u21,6u65,7u51,8
  • Priority: P5
  • Status: Closed
  • Resolution: Fixed
  • OS: linux
  • CPU: x86
  • Submitted: 2010-09-27
  • Updated: 2015-06-04
  • Resolved: 2014-11-06
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 9
9 b39Fixed
Related Reports
Relates :  
Description
FULL PRODUCT VERSION :
Affects all versions I've tried, including
javac 1.6.0_21
javac 1.5.0_22


ADDITIONAL OS VERSION INFORMATION :
Debian GNU/Linux 5.0 (2.6.26-2-686-bigmem i686)

A DESCRIPTION OF THE PROBLEM :
The following classes compile fine with javac:

  public abstract class Base<A> {
      public abstract void go(A a);
      public void go(String s) { System.out.println(s); }
  }
  public class Impl extends Base<String> { }

But the following do not, where the only difference is the order of the method declarations in Base:

  public abstract class Base<A> {
      public void go(String s) { System.out.println(s); }
      public abstract void go(A a);
  }
  public class Impl extends Base<String> { }

resulting in this compile-time error:

Impl.java:1: Impl is not abstract and does not override abstract method go(java.lang.String) in Base
public class Impl extends Base<String> { }
       ^
1 error

According to the JLS3 introduction, "Declaration order is significant only for local variables, local classes, and the order of initializers of fields in a class or interface."  This is such a fundamental feature of Java that clearly the compiler is wrong in at least one case.

The JLS seems inconsistent on this subject.  According to 8.4.8.4, Base.go(String) should override Base.go(A) from the perspective of Impl, but that does not satisfy the strict definition of overriding in 8.4.8.1.


REPRODUCIBILITY :
This bug can be reproduced always.

Comments
The problem is that Types.implementation is not prepared to handle cases where a type defines more than one method that could act as an implementation of a given method in a subclass.
13-10-2014

I'm recategorizing this as a javac bug. The spec is clear, and no error should occur (for either order of declarations).
31-07-2014

Relevant spec for overriding, JLS 8 8.4.8.1, applies: - 'go(String)' is inherited by Impl - 'go(A)' is declared in a superclass of Impl - Impl does not inherit 'go(A)' (per 8.4.8: a "concrete method inherited by C from its direct superclass has a signature that is a subsignature of the signature of m") - 'go(A)' is public So 'go(String)' overrides 'go(A)' from Impl. JLS 7 is not as explicit, but the intent is for overriding to take place, per 8.4.8.4: "the method that is not abstract is considered to override, and therefore to implement, all the other methods on behalf of the class that inherits it."
31-07-2014

As of ecj 3.7, Eclipse matches the (inconsistent) javac behavior: abstract-declared-first is accepted, concrete-declared-first is rejected. javac behavior seems consistent in 6, 7, and 8. Interestingly, if 'go(A)' is the concrete method and 'go(String)' is abstract, no error occurs.
31-07-2014

EVALUATION It is not clear as to whether this program should be rejected because of its ill-formedness. On the one hand, JLS shoul dbe fine with it, as the subclass inherits override-quivalent methods (so no clash should occur). The program could be rewritten as follows: abstract class Base<A> { public abstract void go(A a); } abstract class ExtBase<A> extends Base<A> { public void go(String s) { System.out.println(s); } } public class Impl extends ExtBase<String> { } Note that Eclipse rejects both this and the original program with an error (because Impl is inherithing two unrelated methods). Javac seems to take the other route and, as a general rule, javac seems to accept those cases (with a glitch in the example presented in this CR). Reassigning to spec for further eval. More specifically, is there any problem when two unrelated method in a class hierarchy become override-equivalent because of an extends clause that restricts supertype bounds? Looks weird, esp. if both methods are non-abstract but I'm not sure there's anything in the JLS against such cases.
10-11-2010