问题描述
假设以下API:
package nashorn.test;
public class API {
public static void test(String string) {
throw new RuntimeException("Don't call this");
}
public static void test(Integer... args) {
System.out.println("OK");
}
}
以下Nashorn JavaScript代码段将失败:
The following Nashorn JavaScript snippet will fail:
var API = Java.type("nashorn.test.API");
API.test(1);
将调用第一种方法而不是第二种方法。这是Nashorn引擎中的错误吗?
The first method will be called instead of the second. Is this a bug in the Nashorn engine?
,其中方法重载和varargs被大量使用,并且此问题可能会导致很多麻烦。
For the record, this issue was previously reported on the jOOQ User Group, where method overloading and varargs are used heavily, and where this issue may cause a lot of trouble.
可能有人怀疑这可能与拳击有关。它没有。当我这样做时,问题也会出现
There might be a suspicion that this could have to do with boxing. It doesn't. The problem also appears when I do
public class API {
public static void test(String string) {
throw new RuntimeException("Don't call this");
}
public static void test(Integer... args) {
System.out.println("OK");
}
public static void test(MyType... args) {
System.out.println("OK");
}
}
并且:
public class MyType {
}
然后:
var API = Java.type("nashorn.test.API");
var MyType = Java.type("nashorn.test.MyType");
API.test(new MyType());
推荐答案
为Nashorn编写重载解析机制的人,我总是着迷于人们遇到的角落案件。无论好坏,以下是最终如何调用:
As the guy who wrote the overload resolution mechanism for Nashorn, I'm always fascinated with corner cases that people run into. For better or worse, here's how this ends up being invoked:
Nashorn的重载方法解决方案尽可能模仿Java语言规范(JLS),但允许特定于JavaScript转换也是如此。 JLS表示,在选择调用重载名称的方法时,如果没有适用的固定arity方法,则可以考虑使用变量arity方法仅调用 。通常,从Java test(String)
调用时,不适用于带有 int
的调用,所以将调用 test(Integer ...)
方法。但是,由于JavaScript实际上允许数字到字符串的隐式转换,因此它适用于任何变量arity方法之前。因此观察到的行为。 Arity胜过非转换。如果你添加了一个 test(int)
方法,那么它将在String方法之前被调用,因为它是固定的arity并且比String更具体。
Nashorn's overload method resolution mimics Java Language Specification (JLS) as much as possible, but allows for JavaScript-specific conversions too. JLS says that when selecting a method to invoke for an overloaded name, variable arity methods can be considered for invocation only when there is no applicable fixed arity method. Normally, when invoking from Java test(String)
would not be an applicable to an invocation with an int
, so the test(Integer...)
method would get invoked. However, since JavaScript actually allows number-to-string implicit conversion, it is applicable, and considered before any variable arity methods. Hence the observed behavior. Arity trumps non-conversion. If you added a test(int)
method, it'd be invoked before the String method, as it's fixed arity and more specific than the String one.
你可能会说我们应该改变选择方法的算法。甚至在Nashorn项目之前就已经有了很多想法(甚至在我独立开发Dynalink时)。当前代码(在Nashorn实际构建的Dynalink库中体现)遵循JLS到字母,并且在没有特定于语言的类型转换时将选择与Java相同的方法。然而,一旦你开始放松你的类型系统,事情开始微妙地改变,你放松的越多,他们就会变得越多(而且JavaScript会放松很多),以及任何改变选择算法会有一些别人会遇到的其他奇怪的行为......我只是带着轻松的类型系统,我很害怕。例如:
You could argue that we should alter the algorithm for choosing the method. A lot of thought has been given to this since even before the Nashorn project (even back when I was developing Dynalink independently). Current code (as embodied in the Dynalink library, which Nashorn actually builds upon) follows JLS to the letter and in absence of language-specific type conversions will choose the same methods as Java would. However, as soon as you start relaxing your type system, things start to subtly change, and the more you relax it, the more they'll change (and JavaScript relaxes a lot), and any change to the choice algorithm will have some other weird behavior that someone else will run into… it just comes with the relaxed type system, I'm afraid. For example:
- 如果我们允许将varargs与fixargs一起考虑,我们需要发明一个更具体的关系不同的arity方法,JLS中不存在的东西,因此与它不兼容,并且当JLS规定fixargs调用时,有时会调用varargs。
- 如果我们不允许JS允许的转换(因此强制
test(String)
不适用于int
参数),有些需要将程序转换为调用String方法(例如,执行test(String(x))
以确保x
是一个字符串等。
- If we allowed varargs to be considered together with fixargs, we'd need to invent a "more specific than" relation among differing arity methods, something that doesn't exist in JLS and thus isn't compatible with it, and would cause varargs to sometimes be invoked when otherwise JLS would prescribe fixargs invocation.
- If we disallowed JS-allowed conversions (thus forcing
test(String)
to not be considered applicable to anint
parameter), some JS developers would feel encumbered by needing to contort their program into invoking the String method (e.g. doingtest(String(x))
to ensurex
is a string, etc.
正如你所看到的,无论我们做什么,其他东西都会受到影响;重载方法选择在Java和JS类型系统之间处于紧张的位置,并且对逻辑中的微小变化非常敏感。
As you can see, no matter what we do, something else would suffer; overloaded method selection is in a tight spot between Java and JS type systems and very sensitive to even small changes in the logic.
最后,当你手动s时在重载中选择,你也可以坚持不合格的类型名称,只要在参数位置的包名称的潜在方法签名没有歧义,即
Finally, when you manually select among overloads, you can also stick to unqualified type names, as long as there's no ambiguity in potential methods signatures for the package name in the argument position, that is
API["test(Integer[])"](1);
也应该有效,不需要 java.lang。
前缀。除非你可以重做API,否则这可能会减轻语法噪音。
should work too, no need for the java.lang.
prefix. That might ease the syntactic noise a bit, unless you can rework the API.
HTH,
Attila。
HTH, Attila.
这篇关于使用varargs参数调用重载方法时的Nashorn错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!