Java Comparator的范型類型推導問題


問題

在項目中,有一處地方需要對日期區間進行排序
我需要以日期區間的開始日為第一優先級,結束日為第二優先級進行排序

代碼

我當時寫的代碼如下:

List<Pair<LocalDate, LocalDate>> dateIntervals = new ArrayList<>();
// 省略構造日期區間

dateIntervals.sort(Comparator.comparing(Pair::getLeft).thenComparing(Pair::getRight));

這段看上去很正確的代碼,居然是沒辦法編譯的。
做了一些試驗

dateIntervals.sort(Comparator.comparing(Pair::getLeft));

當僅以日期開始日排序,可以編譯沒問題

那么把Comparator單獨提取出來呢

Comparator<Pair<LocalDate, LocalDate>> cmp = Comparator.comparing(Pair::getLeft);
dateIntervals.sort(cmp);

這樣當然是沒有問題的

Comparator<Pair<LocalDate, LocalDate>> cmp = Comparator.comparing(Pair::getLeft).thenComparing(Pair::getRight);
dateIntervals.sort(cmp);

這樣是沒法編譯的,和我原來的寫法其實沒有本質的區別

Comparator<Pair<LocalDate, LocalDate>> cmp = Comparator.comparing(Pair::getLeft);
cmp = cmp.thenComparing(Pair::getRight);

dateIntervals.sort(cmp);

當我再嘗試把thenComparing分開來寫時,居然又可以通過編譯了

對此我感到很困惑,我在偉大萬能的stackoverflow上翻到了一個類似的問題

這個哥們碰到的問題與我的問題雖然不是同一個,但卻是類似的。

本質的問題是Java語言的類型推導

來看一下Comparator#comparing的源碼

public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
        Function<? super T, ? extends U> keyExtractor)
{
    Objects.requireNonNull(keyExtractor);
    return (Comparator<T> & Serializable)
        (c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
}

再來看一下Comparator#thenComparing的源碼

default <U extends Comparable<? super U>> Comparator<T> thenComparing(
        Function<? super T, ? extends U> keyExtractor)
{
    return thenComparing(comparing(keyExtractor));
}

好的,再回顧一下下面這段可以通過編譯的代碼

Comparator<Pair<LocalDate, LocalDate>> cmp = Comparator.comparing(Pair::getLeft);
dateIntervals.sort(cmp);

由於dateIntervalsList<Pair<LocalDate,LocalDate>>類型,Java編譯器可以根據這個目標類型來進行推導,所以sort函數內的Comparator.comparing應該返回的是Comparator<Pair<LocalDate,LocalDate>>,那么comparing內的函數的入參是Pair<LocalDate,LocalDate>就確定下來了。

再來看一下不能編譯的如下代碼

Comparator<Pair<LocalDate, LocalDate>> cmp = Comparator.comparing(Pair::getLeft).thenComparing(Pair::getRight);
dateIntervals.sort(cmp);

問題是thenComparing返回的Comparator<T>中的T是跟着調用方走的,也就是意味着要得先知道前面一部分 Comparator.comparing(Pair::getLeft)的類型,但是這種情況下前面這一部分沒辦法根據目標類型進行推導,所以類型推導在這里就陷入了一種僵局。

這不得不說是Java語言中類型推導還不夠完美的地方。

那么如何解決這個問題呢,除了上面那種Comparator分兩步走的情況,
直接指定范型類型來調用方法,專治各種范型推導失敗。
關於指定范型類型調用方法的語法規范可以參考JLS 15.12中寫明的方法調用。

所以,最后我寫的語句是如下的:

dateIntervals.sort(Comparator.<Pair<LocalDate, LocalDate>, LocalDate>comparing(Pair::getLeft) .thenComparing(Pair::getRight));


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM