Java:更通用的方法签名给我一个NoSuchMethodError

Jor*_*dan 8 java signature nosuchmethoderror

有一个罐子,当它被创建时应该使用一种方法MyClass.doSomething(List).此方法已更改为doSomething(Collection)并放入另一个jar(仅限此类).

我把第二个jar放在classpath的第一个jar前面,但是当我的第一个jar中的代码MyClass.doSomething()用List 调用时,我仍然得到一个

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

怎么可能?Ant已被用于编译jar.

Joa*_*uer 11

源兼容性和二进制兼容性之间存在重要差异.

  • 如果某些类V1和V2的两个版本是二进制兼容的,那意味着针对V1编译的类将对V2运行良好.
  • 如果某些类V1和V2的两个版本是源兼容的,那意味着可以针对V1编译的类将针对V2进行编译.

正如您所经历的那样,源兼容性并不会自动暗示二进制兼容性.

编译源代码时,要调用的特定方法签名由编译器决定并存储在.class文件中(在本例中为doSomething(List)).

如果更改了类并且在添加时doSomething(List)删除了该方法doSomething(Collection),则会保留源兼容性(因为您可以简单地针对新类编译相同的代码),但二进制兼容性会丢失!

Java语言规范有关于二进制兼容性的整个部分.

总结一下:虽然将方法的参数类型更改为更通用的类型(通常)源兼容,但它不是二进制兼容的.

如果要保留二进制兼容性,则更改必须如下所示:

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);
}
Run Code Online (Sandbox Code Playgroud)


And*_*yle 4

一般来说,如果得到NoSuchMethodError,则意味着运行时使用的目标类的版本与编译调用类所针对的目标类的版本不同。就您而言,这并不是一个令人惊讶的错误;第一个 JAR 中的字节码仍然是针对采用 a 的方法的存在进行编译的List而该方法并不存在

这可能是由于 Ant 的增量编译没有注意到依赖类已更改,或类似的原因。

如果您对整个项目(嗯,至少是您提到的两个 JAR)进行了彻底的重建,那么这个问题应该得到解决,因为编译器会创建调用新doSomething签名的字节码。