为什么 Arrays.sort(T[] a, Comparator<? super T> c) 将 T 推断为二维数组的对象?
Why Arrays.sort(T[] a, Comparator<? super T> c) infers T as Object for a 2d array?
如果我想对二维数组进行排序。 (只需重新排序行,不要触摸每行中的数据)。
在以下代码段中:所有 3 个案例都使用相同的 Arrays.sort(T[] a, Comparator<? super T> c)
方法签名。案例 (a) 工作正常。然而,仅仅通过向第二个参数添加一个if条件,T的推论就会改变。我不明白为什么。
// array contains 3 tuples, sort it by the first element, then second element
int[][] array1 = new int[3][2];
array1[0] = new int[]{1,2};
array1[1] = new int[]{2,3};
array1[2] = new int[]{2,4};
// Case (a): compiles good, tuple is inferred as int[]
Arrays.sort(array1, Comparator.comparingInt(tuple -> tuple[0])); // Arrays.sort(T[] a, Comparator<? super T> c) correctly infers that T refers to int[]
// Case (b.1): compile error: incompatible types
// tuple is now inferred as Object, why?
Arrays.sort(array1,
(a1, a2) -> a1[0] == a2[0] ?
Comparator.comparingInt(tuple -> tuple[1]) : Comparator.comparingInt(tuple -> tuple[0]));
// Case (b.2): compile error: incompatible types
Arrays.sort(array1, Comparator.comparingInt(tuple -> tuple[0]).thenComparingInt(tuple -> tuple[1]));
// Case (c): if downcast tuple[0] to ((int[])tuple)[0], then (b) works fine.
更新:
- 在评论的启发下,我很快意识到案例(b.1)实际上是无效的。
(b.1) 中的 lambda 假设 return 是一个整数,而不是比较器。例如。
Arrays.sort(array1, (a1, a2) -> a1[0] == a2[0] ? 0 : 1);
- 在所有其他情况下,我看到
Comparator.<int[]>comparingInt(...)
正确强制推理。
简短回答:编译器不够智能,无法通过如此复杂的表达式进行推断。它需要一些帮助来推断类型:
Arrays.sort(array1, Comparator.<int[]>comparingInt(tuple -> tuple[0]).thenComparingInt(tuple -> tuple[1]));
相关 JEP:http://openjdk.java.net/jeps/101
至于三元表达式的情况,我认为它需要进一步调整,因为你需要在 lambda 中 return 一个 int
,而不是 Comparator
:
Arrays.sort(array1,
(a1, a2) -> a1[0] == a2[0] ?
Comparator.<int[]>comparingInt(tuple -> tuple[1]).compare(a1, a2) :
Comparator.<int[]>comparingInt(tuple -> tuple[0]).compare(a1, a2));
进一步挖掘后,这里有一个关于此行为的直观解释:
public interface Comparator<T> {
...
default Comparator<T> thenComparingInt(ToIntFunction<? super T> keyExtractor)
public static <T> Comparator<T> comparingInt(ToIntFunction<? super T> keyExtractor)
...
}
public static
方法调用时需要调用者定义T
(第二个方法中第一个<T>
)。否则默认为 Object
.
这与非静态方法不同,其中 T
在实例化期间已经定义。
因此查看 Comparator.comparingInt(tuple -> tuple[0]);
,编译器将不知道 T
是什么,因此默认为 Object
。
为了正确地为编译器提供实际类型 T
,我们可以在不同的地方显式定义 T。
// define T when calling the method
Comparator.<int[]>comparingInt(tuple -> tuple[0]);
// infer by return type, this is samilar to placing the rhs to Arrays.sort(int[][] a, rhs)
Comparator<int[]> c = Comparator.comparingInt(tuple -> tuple[0]);
// explicit target type for lambda expression
Comparator.comparingInt((ToIntFunction<int[]>) tuple -> tuple[0]);
// explicit type for lambda parameter
Comparator.comparingInt((int[] tuple) -> tuple[0]);
// This works but it's doesn't change the inference of T as comments suggests below.
Comparator.comparingInt(tuple -> ((int[])tuple)[0]);
一旦它 returns 一个 Comparator<int[]>
对象(因此定义 T
为 int[]
),实例方法 thenComparingInt
在知道 T 是什么时被调用.
如果我想对二维数组进行排序。 (只需重新排序行,不要触摸每行中的数据)。
在以下代码段中:所有 3 个案例都使用相同的 Arrays.sort(T[] a, Comparator<? super T> c)
方法签名。案例 (a) 工作正常。然而,仅仅通过向第二个参数添加一个if条件,T的推论就会改变。我不明白为什么。
// array contains 3 tuples, sort it by the first element, then second element
int[][] array1 = new int[3][2];
array1[0] = new int[]{1,2};
array1[1] = new int[]{2,3};
array1[2] = new int[]{2,4};
// Case (a): compiles good, tuple is inferred as int[]
Arrays.sort(array1, Comparator.comparingInt(tuple -> tuple[0])); // Arrays.sort(T[] a, Comparator<? super T> c) correctly infers that T refers to int[]
// Case (b.1): compile error: incompatible types
// tuple is now inferred as Object, why?
Arrays.sort(array1,
(a1, a2) -> a1[0] == a2[0] ?
Comparator.comparingInt(tuple -> tuple[1]) : Comparator.comparingInt(tuple -> tuple[0]));
// Case (b.2): compile error: incompatible types
Arrays.sort(array1, Comparator.comparingInt(tuple -> tuple[0]).thenComparingInt(tuple -> tuple[1]));
// Case (c): if downcast tuple[0] to ((int[])tuple)[0], then (b) works fine.
更新:
- 在评论的启发下,我很快意识到案例(b.1)实际上是无效的。
(b.1) 中的 lambda 假设 return 是一个整数,而不是比较器。例如。
Arrays.sort(array1, (a1, a2) -> a1[0] == a2[0] ? 0 : 1);
- 在所有其他情况下,我看到
Comparator.<int[]>comparingInt(...)
正确强制推理。
简短回答:编译器不够智能,无法通过如此复杂的表达式进行推断。它需要一些帮助来推断类型:
Arrays.sort(array1, Comparator.<int[]>comparingInt(tuple -> tuple[0]).thenComparingInt(tuple -> tuple[1]));
相关 JEP:http://openjdk.java.net/jeps/101
至于三元表达式的情况,我认为它需要进一步调整,因为你需要在 lambda 中 return 一个 int
,而不是 Comparator
:
Arrays.sort(array1,
(a1, a2) -> a1[0] == a2[0] ?
Comparator.<int[]>comparingInt(tuple -> tuple[1]).compare(a1, a2) :
Comparator.<int[]>comparingInt(tuple -> tuple[0]).compare(a1, a2));
进一步挖掘后,这里有一个关于此行为的直观解释:
public interface Comparator<T> {
...
default Comparator<T> thenComparingInt(ToIntFunction<? super T> keyExtractor)
public static <T> Comparator<T> comparingInt(ToIntFunction<? super T> keyExtractor)
...
}
public static
方法调用时需要调用者定义T
(第二个方法中第一个<T>
)。否则默认为Object
.这与非静态方法不同,其中
T
在实例化期间已经定义。
因此查看 Comparator.comparingInt(tuple -> tuple[0]);
,编译器将不知道 T
是什么,因此默认为 Object
。
为了正确地为编译器提供实际类型 T
,我们可以在不同的地方显式定义 T。
// define T when calling the method
Comparator.<int[]>comparingInt(tuple -> tuple[0]);
// infer by return type, this is samilar to placing the rhs to Arrays.sort(int[][] a, rhs)
Comparator<int[]> c = Comparator.comparingInt(tuple -> tuple[0]);
// explicit target type for lambda expression
Comparator.comparingInt((ToIntFunction<int[]>) tuple -> tuple[0]);
// explicit type for lambda parameter
Comparator.comparingInt((int[] tuple) -> tuple[0]);
// This works but it's doesn't change the inference of T as comments suggests below.
Comparator.comparingInt(tuple -> ((int[])tuple)[0]);
一旦它 returns 一个 Comparator<int[]>
对象(因此定义 T
为 int[]
),实例方法 thenComparingInt
在知道 T 是什么时被调用.