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

时间:2011-05-12 11:28:47

标签: java signature nosuchmethoderror

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

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

  

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

怎么可能? Ant已被用于编译罐子。

2 个答案:

答案 0 :(得分:11)

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

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

源代码兼容性会自动暗示二进制兼容性,正如您所经历的那样。

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

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

Java语言规范有an entire section on binary compatibility

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

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

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);
}

答案 1 :(得分:4)

通常,如果得到NoSuchMethodError,则表示运行时使用的目标类的版本与编译调用类的目标类的版本不同。在你的情况下,这不是一个令人惊讶的错误;第一个JAR中的字节码仍然是针对存在一个采用List不存在的方法进行编译的。

这可能是由于Ant的渐进式编译没有注意到依赖类已经发生了变化,或类似的东西。

如果你对整个项目进行了一次干净的重建(好,至少是你提到的两个JAR),应该解决这个问题,因为编译器会创建调用新doSomething签名的字节码。