Java: more general method signature gives me a NoSuchMethodError
Asked Answered
S

2

8

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.

Shevat answered 12/5, 2011 at 11:28 Comment(2)
MyClass exists in both jars? What if you change the class name in the 2nd jar to MyOtherClass? Does the problem go away?Nels
yes MyClass exists in both jars. I took it from jar1, modified it, add it to jar2Shevat
T
12

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);
}
Tantalizing answered 12/5, 2011 at 11:38 Comment(1)
Thanks for these explanations. However I forgot to mention that it works on my side but not on his. Is there a way i could have make it possible and not him ?Shevat
I
4

In general, if you get a NoSuchMethodError, it means that the version of the target class used at runtime is different from the version of the target class that the calling class was compiled against. In your case it's not a surprising error; the bytecode in the first JAR is still compiled against the existence of a method that takes a List, which doesn't exist.

This could be down to Ant's incremental compilation not noticing that dependent classes have changed, or something similar.

If you do a clean rebuild of the whole project (well, at least both the JARs you mentioned) this problem should be resolved, as the compiler creates bytecode that calls the new doSomething signature.

Infusionism answered 12/5, 2011 at 11:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.