假定以下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方法之前调用的,因为它是固定不变的,并且比String方法更具体。
您可能会争辩说,我们应该更改用于选择方法的算法。自从甚至在Nashorn项目之前(甚至在我独立开发Dynalink时),就已经对此进行了很多思考。当前代码(Nashorn实际在其上构建的Dynalink库中体现)遵循JLS,并且在没有特定语言的类型转换的情况下,将选择与Java相同的方法。但是,一旦您开始放松类型系统,事情就会开始发生细微变化,而放松程度越多,它们的变化就越大(JavaScript放松
很多 ),并且对选择算法的任何更改都会带来一些其他变化恐怕其他人会遇到奇怪的行为……它只是随随便便的类型系统附带的。例如:
test(String)
将其视为不适用于int
参数),则某些JS开发人员会因需要扭曲其程序以调用String方法而感到困惑(例如test(String(x))
,确保x
使用字符串等)。如您所见,无论我们做什么,都会遭受其他损失;重载方法的选择在Java和JS类型系统之间处于紧要关头,并且即使逻辑上的微小变化也非常敏感。
最后,当您在重载中手动选择时,只要在参数位置对包名称的潜在方法签名没有歧义,也可以坚持使用不合格的类型名称,即
API["test(Integer[])"](1);
应该也可以工作,不需要java.lang.
前缀。除非可以重新编写API,否则这可能会稍微减轻语法噪音。
HTH,阿提拉。
我有两个重载的方法,分别是varargs int和long。当我运行一个传递整数的测试时,它似乎更喜欢varargs long方法。然而,如果我使这些方法是静态的并使用整数运行,它似乎更喜欢varargs int方法。这是怎么回事? 产出: 内长varargs 1 内部静态int varargs 1
问题内容: 如果我有一个vararg Java方法并调用,则我和 都将null。但是,如果我呼叫f,则其本身为null。为什么会这样呢? 我应该怎么称呼这样的? 问题答案: 问题是,当你使用原义null时,Java不知道它应该是哪种类型。它可以是空对象,也可以是空对象数组。对于单个参数,假定为后者。 你有两种选择。将null显式转换为Object或使用强类型变量调用该方法。请参见下面的示例: 输出
问题内容: 在下面的代码中,输出为 串 如果我使用类型为参数的方法删除该方法,则输出为 目的 我知道当参数类型不完全匹配时方法的重载行为如何,但是我不明白如何将 null 视为和/或参数。 这有什么解释? 问题答案: 根据Java规范 15.12.2.5节,它始终使用最特定的方法。 简介相当具体: 如果多个成员方法既可访问又可应用于方法调用,则必须选择一个成员方法来为运行时方法分派提供描述符。Ja
我试图在Spock中模拟。斯波克失败了 MyTest.groovy:
问题内容: 这将无法编译: 这将编译并工作: 第一个和第二个示例非常相似。首先使用varargs,第二个不使用。为什么一个有效,第二个无效。7是原始的,因此在两种情况下都应调用第二种方法。这是正常行为吗? 我找到了: 错误报告 堆栈溢出 问题答案: 这是正在发生的情况的高层非正式摘要。 首先,varargs语法实际上只是传递数组的语法加糖。所以实际上是要传递一个…数组。 但是数组是什么?这里有两个
我需要从使用vararg参数的Java接口重写Java方法: 当我尝试在Scala中实现该方法时,如下所示 我在运行时得到一个"java.lang.AbstractMEDError: null"错误。 这是我已经尝试过的: > 使用Scala注释,它应该生成一个Java友好的委托方法= def apply(args:Array[Object]):Unit=apply(args:*) 它实际上被成功