why arrays.sort(t[]a,comparator< ?super t>c)推断t是二维数组的对象?

djmepvbi  于 2021-07-09  发布在  Java
关注(0)|答案(2)|浏览(399)

如果我想对二维数组排序(只需对行进行重新排序,不要触摸每行中的数据)。
在下面的代码片段中:所有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假设返回一个整数,而不是一个比较器。例如 Arrays.sort(array1, (a1, a2) -> a1[0] == a2[0] ? 0 : 1); 在所有其他情况下,我明白了 Comparator.<int[]>comparingInt(...) 强制正确推断。

sycxhyv7

sycxhyv71#

在进一步挖掘之后,以下是我对这种行为的直观解释:

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]);

一旦它返回 Comparator<int[]> 对象(如此定义 T 作为 int[] ),示例方法 thenComparingInt 被要求知道t是什么。

ldfqzlk8

ldfqzlk82#

简而言之:编译器不够聪明,无法通过如此复杂的表达式进行推断。它需要一些帮助来推断类型:

Arrays.sort(array1, Comparator.<int[]>comparingInt(tuple -> tuple[0]).thenComparingInt(tuple -> tuple[1]));

相关jep:http://openjdk.java.net/jeps/101
至于三元表达式的情况,我认为它需要进一步修改,因为您需要返回一个 int 在lambda,不是一个 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));

相关问题