【问题标题】:Changing a constructor param type breaks class in another jar更改构造函数参数类型会破坏另一个 jar 中的类
【发布时间】:2009-06-16 14:43:07
【问题描述】:

我在一个普通的 jar 中有以下类:

public class Common 
{
  public Common(List list)
  {
    ...  
  }  
}

然后我将构造函数参数从List 更改为Collection,如下所示:

public class Common 
{
  public Common(Collection collection)
  {
    ...
  }
}

重建公共 jar 并运行系统在调用构造函数时会在任何依赖类中导致 NoSuchMethodError,直到我重新编译该类。

我有一些想法是什么导致了这种情况,以及构造函数如何绑定在依赖类的字节码中,但我不是 100% 确定。

请问有人能解释一下这里发生了什么吗?

更新

我随后做了一个快速测试并查看了字节码:

Compiled from "Client.java"
public class Client extends java.lang.Object{
public Client();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[]);
  Code:
   0:   new #2; //class ArrayList
   3:   dup
   4:   invokespecial   #3; //Method java/util/ArrayList."<init>":()V
   7:   astore_1
   8:   new #4; //class Common
   11:  dup
   12:  aload_1
   13:  invokespecial   #5; //Method Common."<init>":(Ljava/util/List;)V
   16:  pop
   17:  return

}

就像 Tom 所说的,正如您在第 13 行看到的那样,确切的构造函数是在编译时绑定的。

你每天都会学到新东西:-)

【问题讨论】:

  • 我应该提到,这是在 Java 1.4.2_11 上运行的
  • 如果你想让你的 Common 类向后兼容,只需包含一个重定向到 Common(Collection) 构造函数的 Common(List) 构造函数。

标签: java binding parameters constructor binary-compatibility


【解决方案1】:

javac 准确解析在编译时调用哪个方法或构造函数。这不会在链接时发生。由于构造函数签名已更改,链接步骤无法找到请求的方法,因此会引发错误。您可以通过提供给构造函数来修复错误——一个接受Collection,另一个接受List。稍后可以添加一个采用Iterable 的构造函数。

注意,泛型类型不构成签名的一部分,因此可以在保持二进制兼容性的同时更改它们。参数和返回类型都是方法签名的一部分(协变返回会导致创建合成桥方法)。

有一个nice big section in the JLS 准确定义了二进制兼容更改的构成。

【讨论】:

  • 谢谢汤姆,这就是我的怀疑。我从刚刚完成的快速测试中添加了字节码,显示(在第 13 行)对具有列表参数的构造函数的显式调用。
【解决方案2】:

您是否导入了正确的 List 和 Collection 类?即java.util.Listjava.util.Collection?

【讨论】:

  • 是的,所有的导入都是正确的。损坏的类无需任何更改即可编译并在此后正常工作。
【解决方案3】:

我认为这可能是库版本的问题。您确定在同一上下文中的其他地方没有其他版本的公共库吗?

【讨论】:

  • classpath 是正确的,上面只有一个 common jar 的副本。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-05-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-11
  • 1970-01-01
  • 2015-11-05
相关资源
最近更新 更多