JDK-6519118 : Classes are compiled but java.lang.AbstractMethodError is thrown at runtime
  • Type: Bug
  • Component: specification
  • Sub-Component: language
  • Affected Version: 6
  • Priority: P4
  • Status: Closed
  • Resolution: Fixed
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2007-01-30
  • Updated: 2014-02-26
  • Resolved: 2011-07-21
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 rcFixed
Related Reports
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.6.0"
Java(TM) SE runtime environment (build 1.6.0-b105)
JavaHotSpot(TM) Client VM (build 1.6.0-b105, mixed mode, sharing)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows XP [Version 5.1.2600]

A DESCRIPTION OF THE PROBLEM :
Classes are compiled without errors but java.lang.AbstractMethodError is thrown at runtime

API Documentation says:
Thrown when an application tries to call an abstract method.
 Normally, this error is caught by the compiler; this error can
 only occur at run time if the definition of some class has
 incompatibly changed since the currently executing method was last
 compiled.

In my example error was NOT caught by the compiler and NO class has
 incompatibly changed since the currently executing method was last
 compiled.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile and run my code.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
There must be an error at time of compilation or NO exception must be thrown at runtime.
ACTUAL -
In my example error was NOT caught by the compiler and NO class has
 incompatibly changed since the currently executing method was last
 compiled.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
Exception in thread "main" java.lang.AbstractMethodError: p1.AbstractClass.doIt()V
	at p1.Test.main(Test.java:6)

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
/** src/p1/AbstractClass .java **/

package p1;

public abstract class AbstractClass {
	abstract void doIt();
}

/** src/p2/AbstractClass2 .java **/
package p2;

import p1.AbstractClass;

public abstract class AbstractClass2 extends AbstractClass {
	public void doIt(){
		
	}
}

/** src/p1/RealClass .java **/
package p1;

import p2.AbstractClass2;

public class RealClass extends AbstractClass2{
	
}


/** src/p1/Test .java **/
package p1;

public class Test {
	public static void main(String[] args) {
		AbstractClass c = new RealClass();
		c.doIt();
	}
}

---------- END SOURCE ----------

Comments
EVALUATION By JLS 8.4.8.1: - AbstractClass2 does not inherit doIt from AbstractClass - AbstractClass2.doIt does not override AbstractClass.doIt - RealClass does inherit AbstractClass2.doIt - RealClass does not override AbstractClass.doIt The problem is dynamic method lookup in JLS 15.12.4.4: If class <RealClass> contains a declaration for a non-abstract method named <doIt> with the same descriptor ... required by the method invocation ..., then: - If the invocation mode is virtual, and the declaration in <RealClass> overrides <AbstractClass.doIt>, then the method declared in <RealClass> is the method to be invoked, and the procedure terminates. The AbstractMethodError occurs because method lookup sees RealClass.doIt, rejects it for not overriding AbstractClass.doIt, recurses up to AbstractClass2, finds doIt there but rejects it again, and finally recurses up to AbstractClass and finds an abstract doIt. (If AbstractClass.doIt is not abstract (but is package-private), then 15.12.2.4 is already satisfactory. It rejects RealClass.doIt, looks up doIt in AbstractClass2, rejects that also, and finally looks up doIt in AbstractClass, and invokes that.) I plan to augment the clause above in 15.12.4.4: - If the invocation mode is virtual, and the declaration in S <RealClass> does not override X.m <AbstractClass.doIt>, and moreover X.m <AbstractClass.doIt> is declared abstract, then an AbstractMethodError is thrown. And modify the promise made in the JLS - "The above procedure (if it terminates without error) will find a non-abstract, accessible method to invoke, provided that all classes and interfaces in the program have been consistently compiled." NB It might appear that we can detect the possibility of an AbstractMethodError before entering the lookup procedure, just by inspecting the doIt declaration in X <AbstractClass> and S <RealClass>. However, if we add a subclass of AbstractClass in p1, e.g. AbstractClass1, that has a non-abstract doIt, and we make p2.AbstractClass2 extend p1.AbstractClass1, then c.doIt in Test executes without error. Just because RealClass.doIt does not override an invoked abstract method doesn't preclude classes above RealClass from overriding it.
31-01-2007

EVALUATION The compiler started to accept this program in 1.4.x. In 1.3 it would reject it. The abstract method AbstractClass.doIt is not a member of AbstractClass2. Consequently it is not a member of RealClass. So how can RealClass implement it? It should probably be a compile time error but the specification is vague. I'm assigning this to java/specification. Please redispatch to java/compiler once the issue is resolved.
31-01-2007