使用方法引用和原始类型的函数接口特化的重载解析
Overload resolution with method references and function interface specializations for primitive types
假设我们有一个 class 和一个重载函数:
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;
}
}
并且我想用一个方法引用来调用 g 类型 A 的函数 -> 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
比 ToDoubleFunction
更适合 toInt
。然而,我对 JLS 的解读是,它应该被拒绝,因为没有理由更具体。
我不得不承认我对 JLS 规范有点迷茫,希望得到一些帮助。我首先想计算 Main::double2int
的类型,所以我查看了 15.13.2. Type of a Method Reference。它没有定义类型,但它定义了类型在不同上下文中何时兼容:
A method reference expression is compatible in an assignment context,
invocation context, or casting context with a target type T if T is a
functional interface type (§9.8) and the expression is congruent with
the function type of the ground target type derived from T.
接地类型为ToIntFunction<A>
和ToDoubleFunction<A>
。 toInt
returns 是一个与 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
的两个重载中的哪一个更具体编译时声明 A -> int
.
的参数 Main::toInt
A functional interface type S is more specific than a functional interface type T for an expression e if T is not a subtype of S and one of the following is true (where U1 ... Uk and R1 are the parameter types and return type of the function type of the capture of S, and V1 ... Vk and R2 are the parameter types and return type of the function type of T):
...
If e is an exact method reference expression (§15.13.1), then i) for
all i (1 ≤ i ≤ k), Ui is the same as Vi, and ii) one of the following
is true:
R2 is void.
R1 <: R2.
R1 is a primitive type, R2 is a reference type, and the compile-time
declaration for the method reference has a return type which is a
primitive type.
R1 is a reference type, R2 is a primitive type, and the compile-time
declaration for the method reference has a return type which is a
reference type.
第一个条件显然不符合,因为R1和R2不void
两个接口 ToIntFunction
和 ToDoubleFunction
的区别仅在于它们的 return 类型是原始类型 double
和 int
。对于原始类型,"R1 <: R2" 子句根据类型的大小在 4.10.1 中定义。 double
和int
之间没有关系,所以本例没有定义哪个类型更具体。
最后两点也不符合,因为两个功能接口中的 none 具有引用类型的 return 值。
两个函数接口 return 原语和代码应该被拒绝为含糊不清的情况似乎没有规则。但是,javac 接受代码,我希望它这样做。所以我想知道这是否是 JLS 中的一个缺失点。
For primitive types, the clause "R1 <: R2" is defined in 4.10.1 according to the size of the types. There is no relation between double and int, so this case does not define which type is more specific.
事实并非如此; double
实际上是 int
的超类型。
The supertypes of a type are obtained by reflexive and transitive
closure over the direct supertype relation, written S >₁ T
, which is
defined by rules given later in this section. We write S :> T
to
indicate that the supertype relation holds between S
and T
.
The following rules define the direct supertype relation among the
primitive types:
double >₁ float
float >₁ long
long >₁ int
超类型关系是直接超类型关系的自反和传递闭包,意味着从(double >₁ float) ∧ (float >₁ long) ∧ (long >₁ int)
开始double :> int
.
假设我们有一个 class 和一个重载函数:
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;
}
}
并且我想用一个方法引用来调用 g 类型 A 的函数 -> 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
比 ToDoubleFunction
更适合 toInt
。然而,我对 JLS 的解读是,它应该被拒绝,因为没有理由更具体。
我不得不承认我对 JLS 规范有点迷茫,希望得到一些帮助。我首先想计算 Main::double2int
的类型,所以我查看了 15.13.2. Type of a Method Reference。它没有定义类型,但它定义了类型在不同上下文中何时兼容:
A method reference expression is compatible in an assignment context, invocation context, or casting context with a target type T if T is a functional interface type (§9.8) and the expression is congruent with the function type of the ground target type derived from T.
接地类型为ToIntFunction<A>
和ToDoubleFunction<A>
。 toInt
returns 是一个与 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
的两个重载中的哪一个更具体编译时声明 A -> int
.
Main::toInt
A functional interface type S is more specific than a functional interface type T for an expression e if T is not a subtype of S and one of the following is true (where U1 ... Uk and R1 are the parameter types and return type of the function type of the capture of S, and V1 ... Vk and R2 are the parameter types and return type of the function type of T):
...
If e is an exact method reference expression (§15.13.1), then i) for all i (1 ≤ i ≤ k), Ui is the same as Vi, and ii) one of the following is true:
R2 is void.
R1 <: R2.
R1 is a primitive type, R2 is a reference type, and the compile-time declaration for the method reference has a return type which is a primitive type.
R1 is a reference type, R2 is a primitive type, and the compile-time declaration for the method reference has a return type which is a reference type.
第一个条件显然不符合,因为R1和R2不void
两个接口 ToIntFunction
和 ToDoubleFunction
的区别仅在于它们的 return 类型是原始类型 double
和 int
。对于原始类型,"R1 <: R2" 子句根据类型的大小在 4.10.1 中定义。 double
和int
之间没有关系,所以本例没有定义哪个类型更具体。
最后两点也不符合,因为两个功能接口中的 none 具有引用类型的 return 值。
两个函数接口 return 原语和代码应该被拒绝为含糊不清的情况似乎没有规则。但是,javac 接受代码,我希望它这样做。所以我想知道这是否是 JLS 中的一个缺失点。
For primitive types, the clause "R1 <: R2" is defined in 4.10.1 according to the size of the types. There is no relation between double and int, so this case does not define which type is more specific.
事实并非如此; double
实际上是 int
的超类型。
The supertypes of a type are obtained by reflexive and transitive closure over the direct supertype relation, written
S >₁ T
, which is defined by rules given later in this section. We writeS :> T
to indicate that the supertype relation holds betweenS
andT
.
The following rules define the direct supertype relation among the primitive types:
double >₁ float
float >₁ long
long >₁ int
超类型关系是直接超类型关系的自反和传递闭包,意味着从(double >₁ float) ∧ (float >₁ long) ∧ (long >₁ int)
开始double :> int
.