假设我们有一个类和一个重载函数:
public class Main {
static final class A {
}
public static String g(ToIntFunction<? extends A> f) {
return null;
}
public static String g(ToDoubleFunction<? extends A> f) {
return null;
}
}
我希望用类型A的函数的方法引用来调用g - &gt; INT:
public class Main {
static final class A {
}
public static String g(ToIntFunction<? extends A> f) {
return null;
}
public static String g(ToDoubleFunction<? extends A> f) {
return null;
}
private static int toInt(A x) {
return 2;
}
public static void main(String[] args) {
ToIntFunction<? extends A> f1 = Main::toInt;
ToDoubleFunction<? extends A> f2 = Main::toInt;
g(Main::toInt);
}
}
这适用于javac,但不适用于eclipse ecj。我向ecj提交了一个错误报告,但我不确定这是否是一个ecj或javac错误,并尝试按照重载解析算法来解决它。我的感觉是代码应该被接受,因为ToIntFunction
与toInt
比ToDoubleFunction
更好地匹配是直观合理的。但是,我对JLS的解读是它应该被拒绝,因为没有理由让一个更具体。
我必须承认我在JLS规范中有点迷失并且会感激一些帮助。我首先想要计算Main::double2int
的类型,所以我查看了15.13.2. Type of a Method Reference。它没有定义类型,但它定义了何时类型在不同的上下文中兼容:
方法引用表达式在赋值上下文中兼容, 如果T是a,则调用上下文或者使用目标类型T转换上下文 功能接口类型(§9.8)和表达式是一致的 从T。
派生的地面目标类型的函数类型
地面类型为ToIntFunction<A>
和ToDoubleFunction<A>
。 toInt
返回一个与double兼容的赋值的int,因此我得出结论,方法引用在调用上下文中可以与ToIntFunction<? extends A>
和ToDoubleFunction<? extends A>
一起使用。这可以通过在主函数中接受ToIntFunction<? extends A>
和ToDoubleFunction<? extends A>
的方法引用来验证。
然后我查看了重载决策,找到了15.12.2.5. Choosing the Most Specific Method,它有一个特殊的方法引用案例来决定ToIntFunction
或ToDoubleFunction
的两个重载中哪一个对参数更具体{ {1}}编译时声明Main::toInt
。
如果T不是S的子类型且下列之一为真,则功能接口类型S比表达式e的功能接口类型T更具体(其中U1 ... Uk和R1是参数类型和返回类型为S的捕获函数类型,V1 ... Vk和R2是T)函数类型的参数类型和返回类型:
...
如果e是精确的方法参考表达式(§15.13.1),那么i)for 所有i(1≤i≤k),Ui与Vi相同,ii)以下之一 是的:
R2无效。
R1&lt;:R2。
R1是基本类型,R2是引用类型,而编译时 方法引用的声明的返回类型是a 原始类型。
R1是引用类型,R2是基本类型,而编译时 方法引用的声明的返回类型是a 参考类型。
第一个条件显然不匹配,因为R1和R2不是无效的。
两个接口A -> int
和ToIntFunction
的区别仅在于它们的原始类型ToDoubleFunction
和double
的返回类型。对于基本类型,根据类型的大小,在4.10.1中定义了“R1&lt ;: R2”子句。 int
和double
之间没有关系,因此这种情况不会定义哪种类型更具体。
最后两点不合适,因为两个功能接口都没有引用类型的返回值。
当两个功能接口返回原语并且代码应该被拒绝为模糊时,似乎没有规则。但是,javac接受了代码,我希望它能够这样做。所以我想知道这是否是JLS中的缺失点。
答案 0 :(得分:3)
对于基本类型,根据类型的大小,在4.10.1中定义了“R1&lt ;: R2”子句。 double和int之间没有关系,所以这种情况不能定义哪种类型更具体。
事实并非如此;事实上,double
是int
的超类型。
一种类型的超类型是通过反身和传递获得的 关于直接超类型关系的闭包,写成
S >₁ T
,即 由本节后面给出的规则定义。我们写S :> T
给 表示超类型关系在S
和T
之间保持不变。
以下规则定义了之间的直接超类型关系 原始类型:
double >₁ float
float >₁ long
long >₁ int
超类型关系是直接超类型关系的自反和传递闭包,意味着(double >₁ float) ∧ (float >₁ long) ∧ (long >₁ int)
跟随double :> int
。