Java:方法名称/签名解析是否静态完成(编译时)?
今天我遇到了一个有趣的问题,我认为这在 Java 中是不可能的。我针对 jgroups 2.6 版本编译了 java 代码,但在运行时使用了 2.12 版本(tomcat Web 应用程序部署)。我收到以下错误
org.jgroups.Message.<init>(Lorg/jgroups/Address;Lorg/jgroups/Address;Ljava/io/Serializable;)
假设 API 从那时起会发生变化,我想将我的代码移植到 jgroups-2.12,但令我惊讶的是,代码用 jgroups-2.12 编译得很好,当我替换新的 jar 时(没有更改单个在我的代码中,只需针对 jgroups-2.12 而不是 jgroups-2.6 进行编译),它工作得很好。
后来我发现2.6中的构造函数Message(Address, Address, Serialized)
在2.12中改为了Message(Address, Address, Object)。这意味着在运行时,JVM 试图找到完全相同的方法,但未能成功。
这是否意味着 Java 编译器在编译时嵌入了精确的方法名称和精确的参数,而具有更广泛参数的方法将不起作用?
I encountered an interesting problem today which I thought was not possible in Java. I compiled my java code against version 2.6 of jgroups but used version 2.12 at runtime (tomcat web app deployment). I got the following error
org.jgroups.Message.<init>(Lorg/jgroups/Address;Lorg/jgroups/Address;Ljava/io/Serializable;)
Assuming that the API would have change since then, I thought of porting my code to jgroups-2.12, but to my surprise the code compiled fine with jgroups-2.12 and when I replaced the new jar (without changing a single line in my code, just compiling against jgroups-2.12 instead of jgroups-2.6), it worked perfectly fine.
I later realized that the constructor Message(Address, Address, Serializable)
in 2.6 was changed to Message(Address, Address, Object) in 2.12. This means at runtime, the JVM was trying to locate the exact same method and was failing to do so.
Does this mean that Java compiler embeds the exact method name and precise arguments while compiling and a method with broader arguments won't work?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
是的,这是完全正确的 - 确切的签名是在编译时绑定的,这就是字节码中包含的内容。
事实上,这甚至包括返回类型,它不包含在用于重载目的的签名中。
从根本上讲,如果您对现有公共 API 成员进行任何更改,这将是一个重大更改。您可以进行一些仅针对语言的更改,例如将
String[]
参数更改为String...
参数,或引入泛型(在某些情况下) 情况下,如果擦除与之前的代码兼容),但仅此而已。Java 语言规范的 第 13 章 都是关于二进制的兼容性 - 阅读该内容以获取更多信息。但特别是来自 第 13.4.14 节:
Yes, that's exactly right - the exact signature is bound at compile-time, and that's what gets included in the bytecode.
In fact, this even includes the return type, which isn't included in signatures for things like overloading purposes.
Fundamentally, if you change anything about an existing public API member, that will be a breaking change. You can get away with some language-only changes, such as changing a
String[]
parameter to aString...
parameter, or introducing generics (in some cases, if the erasure is compatible with the previous code), but that's pretty much it.Chapter 13 of the Java Language Specification is all about binary compatibility - read that for more information. But in particular, from section 13.4.14:
确切地。您还可以从收到的错误消息中看到这一点:
此处包含完整的签名,运行时会寻找完美匹配。
在其他几种情况下,更改 API 会破坏 Java 中的二进制兼容性,但不会破坏源兼容性,例如,当您将基本类型更改为其盒装变体时,反之亦然。正如 Jon 所指出的,只有泛型中的更改(但不是所有更改)和使用 VarArgs 语法不会影响运行时方法解析,因为两者都只是编译器功能,不会影响字节码。
这也意味着,当您在新的库版本中引入方法的重载时,该重载将仅由使用新版本编译的调用者使用。旧的二进制文件仍然会调用旧的方法,即使它们的参数类型更适合新的重载。
因此,对于库设计者来说,有时建议不要更改现有方法的签名,而只添加新的重载(并让旧方法转发到新方法,以便调用哪一个方法并不重要)。当然,缺点是所有这些重载都掩盖了真正的 API,并使理解 API 变得更加困难。
Exactly. You can also see this from the error message you got:
The complete signature is contained here, and the runtime looks for a perfect match.
There are also several other occasions when changing an API breaks binary compatibility in Java, but not source compatibility, for example when you change a primitive type to its boxed variant or vice versa. As pointed out by Jon, only changes in Generics (but not even all changes) and using the VarArgs syntax do not affect runtime method resolution, as both are only compiler features and do not affect the bytecode.
This also means that when you introduce an overload of a method in a new library version, this overload will only be used by callers compiled with the new version. Old binaries will still call the old method, even if their argument types would better fit the new overload.
For library designers it is thus sometimes advisable to do not change the signatures of existing methods, but only add new overloads (and let the old methods forward to the new ones so that it doesn't matter which one is called). Of course the disadvantage is that all these overloads obscure the real API and and make understanding the API more difficult.
Java 编译器必须在编译文件中放入准确的方法名称和精确的参数,以便稍后确定要加载哪个类以及要调用哪个方法。否则无法精确调用所请求的方法。
Well Java compiler has to put exact method name and precise arguments in the compiled file to figure out later which class to load and which method to call. As else there is no way to precisely call the requested method.