使用varargs参数调用重载方法时出现Nashorn错误
Nashorn bug when calling overloaded method with varargs parameter
假设以下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片段将失败:
var API = Java.type("nashorn.test.API");
API.test(1);
将调用第一个方法而不是第二个方法。这是Nashorn引擎的错误吗?
为了记录在案,这个问题以前在jOOQ用户组上有报道,那里大量使用方法重载和varargs,这个问题可能会造成很多麻烦。
关于拳击
可能有人怀疑这可能与拳击有关。事实并非如此。当我进行时,问题也会出现
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编写过载解决机制的人,我总是对人们遇到的角落案例着迷。不管是好是坏,这是如何被调用的:
Nashorn的重载方法解析尽可能地模仿Java语言规范(JLS),但也允许JavaScript特定的转换。JLS表示,当为重载名称选择要调用的方法时,当没有适用的固定arity方法时,只能考虑使用变量arity方法来调用。通常,从Java调用时,test(String)
不适用于具有int
的调用,因此会调用test(Integer...)
方法。然而,由于JavaScript实际上允许数字到字符串的隐式转换,因此它是适用的,并且在任何变量arity方法之前都要考虑。因此观察到的行为。Arity胜过不皈依。如果添加了test(int)
方法,它将在String方法之前被调用,因为它是固定的arity,并且比String方法更具体。
你可能会争辩说,我们应该改变选择方法的算法。甚至在Nashorn项目之前(甚至在我独立开发Dynalink的时候),人们就已经对此进行了大量思考。当前代码(具体体现在Dynalink库中,Nashorn实际上是在该库的基础上构建的)完全遵循JLS,在没有特定于语言的类型转换的情况下,将选择与Java相同的方法。然而,一旦你开始放松你的类型系统,事情就开始发生微妙的变化,你放松得越多,它们就会改变得越多(JavaScript会放松很多),对选择算法的任何改变都会有其他人会遇到的其他奇怪行为……恐怕这只是放松类型系统带来的。例如:
- 如果我们允许varargs与fixargs一起考虑,我们需要在不同的arity方法之间创造一种"比"更具体的关系,这种关系在JLS中不存在,因此与它不兼容,并且会导致varargs有时被调用,否则JLS会规定fixargs调用
- 如果我们禁止JS允许的转换(从而迫使
test(String)
不适用于int
参数),一些JS开发人员会因为需要扭曲他们的程序来调用String方法(例如,执行test(String(x))
以确保x
是字符串,等等)而感到不安
正如你所看到的,无论我们做什么,其他事情都会受到影响;重载方法选择在Java和JS类型系统之间处于一个紧张的位置,并且对逻辑中的微小变化非常敏感。
最后,当您在重载中手动选择时,您也可以坚持使用不合格的类型名称,只要在参数位置的包名称的潜在方法签名中没有歧义,即
API["test(Integer[])"](1);
也应该工作,不需要java.lang.
前缀。这可能会稍微减轻语法噪音,除非您可以修改API。
HTH,阿提拉。
这些都是有效的解决方法:
使用数组参数显式调用test(Integer[])
方法:
var API = Java.type("nashorn.test.API");
API.test([1]);
消除过载:
public class AlternativeAPI1 {
public static void test(Integer... args) {
System.out.println("OK");
}
}
正在删除varargs:
public class AlternativeAPI3 {
public static void test(String string) {
throw new RuntimeException("Don't call this");
}
public static void test(Integer args) {
System.out.println("OK");
}
}
将String
替换为CharSequence
(或任何其他"类似类型"):
public class AlternativeAPI2 {
public static void test(CharSequence string) {
throw new RuntimeException("Don't call this");
}
public static void test(Integer args) {
System.out.println("OK");
}
}
这是一个不明确的情况。第二种情况是寻找一个整数数组或一个以上的整数来区分第一种情况。您可以使用方法选择来告诉Nashorn您的意思是哪种情况。
API["test(java.lang.Integer[])"](1);
- Node.js v6.2.0类扩展不是函数错误
- Jquery菜单操作不稳定,定位不正确,存在一般错误
- document.open/document.write没有正确地清除chrome中的文档——这是chrome的错误吗
- 试图在引导模式内动态生成图表,得到offsetWidth错误
- 为什么会出现错误;未捕获的类型错误:undefined不是函数;
- 我如何修复包含在captcha的addthis中的错误
- 同样,同样的错误'ahorcado.js:26未捕获类型错误:无法读取属性'beginPath'
- 节点是否需要模块传递带有方括号的arg?这是个错误吗
- Webpack/Rect:遵循egghead.io教程,但出现错误:您可能需要一个合适的加载程序来处理此文件类型
- CKFinder 3为所选文件返回错误的URL
- 同位素库错误:未捕获错误无布局模式包装生产线8
- 铬:“;未捕获的语法错误:意外的标记:"
- 如何通过自己获得Chrome扩展的用户反馈/错误报告
- 相位器状态未捕获参考错误
- /undefined在我的404错误日志中多次出现
- Javascript未捕获语法错误意外的标识符错误
- javascript:如何在antlr生成的Lexer中进行错误处理
- Angularjs工厂注入错误
- 可以设置“;文件名"发生错误时显示的内联脚本标记的
- 使用varargs参数调用重载方法时出现Nashorn错误