• 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));
    
  • 相关阅读:
    linux源码方式安装Apache
    linux的chmod,chown命令详解
    2011年10月18日
    mysql检查查询及索引效率方法(explain)
    php中英文字符串的研究
    2011年10月20日
    PHP JSON中文乱码解决方法大全
    解决PHP下载文件名中文乱码
    php字符串学习笔记
    CSU_BMW正式组队纪念赛出题+部分解题报告
  • 原文地址:https://www.cnblogs.com/micrari/p/6092280.html
Copyright © 2020-2023  润新知