有一个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已被用于编译罐子。
答案 0 :(得分:11)
源兼容性和二进制兼容性之间存在重要差异。
源代码兼容性不会自动暗示二进制兼容性,正如您所经历的那样。
编译源代码时,要调用的特定方法签名由编译器决定并存储在.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
签名的字节码。