Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java: more general method signature gives me a NoSuchMethodError

There is a jar that when it has been created was supposed to use a method MyClass.doSomething(List). This method has been changed to doSomething(Collection) and put into another jar (with this class only).

I put my 2nd jar in front of my first jar in the classpath, but when the code inside my first jar is calling MyClass.doSomething() with a List I still get a

java.lang.NoSuchMethodError: MyClass.doSomething(Ljava/util/List;)Ljava/util/List;

How is that possible ? Ant has been used to compile the jars.

like image 666
Jordan Avatar asked May 12 '11 11:05

Jordan


1 Answers

There is an important difference between source compatibility and binary compatibility.

  • If two versions of some class V1 and V2 are binary compatible, that means that classes compiled against V1 will run just fine against V2.
  • If two versions of some class V1 and V2 are source compatible, that means that a class that could compile against V1 will compile just fine against V2.

Source compatibility does not automatically imply binary compatibility, as you've experienced.

When the source is compiled, the specific method signature to be called is decided by the compiler and stored in the .class file (in this case it's doSomething(List)).

If the class is changed and the method doSomething(List) is removed while doSomething(Collection) is added, then source compatibility is retained (because you can simply compile the same code against the new class), but binary compatibility is lost!

The Java Language Specification has an entire section on binary compatibility.

To summarize: while changing the argument type of a method to a more general type is (usually) source compatible, it is not binary compatible.

If you want to retain binary compatibility, then the change would have to look like this:

public void doSomething(Collection foo) { ... } // original method with changed argument type

public void doSomething(List foo) { // new binary compatibility method, just delegates to original one
  doSomething((Collection) foo);
}
like image 178
Joachim Sauer Avatar answered Oct 07 '22 21:10

Joachim Sauer