问题

在项目中,有一处地方需要对日期区间进行排序

我需要以日期区间的开始日为第一优先级,结束日为第二优先级进行排序

代码

我当时写的代码如下:

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

Java Comparator的范型类型推导问题的更多相关文章

  1. Java数据结构与算法分析-第一章(引论)-Java中的范型<T,E>构件

    一.为什么需要使用范型? 官方的说法是:Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型. 泛型的本质 ...

  2. Java范型

    泛型不用考虑对象的具体类型.优点在于,因为不用考虑对象的具体类型所以可以对一类对象执行一定的相同操作:缺点在于,因为没有考虑对象的具体类型所以就不能使用对象自带的接口函数.泛型的最佳用同是实现容器类. ...

  3. 关于java范型

    1 范型只在编译阶段有效 编译器在编译阶段检查范型结果之后,就会将范型信息删除.范型信息不会进入运行时阶段. 泛型类型在逻辑上看以看成是多个不同的类型,实际上都是相同的基本类型. 2 不能对确定的范型 ...

  4. C# 范型约束 new() 你必须要知道的事

    C# 范型约束 new() 你必须要知道的事 注意:本文不会讲范型如何使用,关于范型的概念和范型约束的使用请移步谷歌. 本文要讲的是关于范型约束无参构造函数 new 的一些底层细节和注意事项.写这篇文 ...

  5. Java范型随笔

    最近在帝都好无聊啊, 排遣寂寞就只有让自己不要停下来,不断的思考了 QWQ; 最近做ndk, java有点忘了,突然看到了一些java范型方面的问题, 踌躇了一会, 想着想着,决定还是写个随笔记录下来 ...

  6. Java数组协变与范型不变性

    变性是OOP语言不变的大坑,Java的数组协变就是其中的一口老坑.因为最近踩到了,便做一个记录.顺便也提一下范型的变性. 解释数组协变之前,先明确三个相关的概念,协变.不变和逆变. 一.协变.不变.逆 ...

  7. Java范型学习笔记

    对于范型的使用或者说印象只有集合,其他地方即使使用过也不知道,反正就是只停留在List<E> Map<K, V>,最近刚好闲来无事,就找找资料学习一下:下列为个人学习总结,欢迎 ...

  8. 为什么Java不允许创建范型数组

    问题示例 List<Integer>[] intListArr = new ArrayList<Integer>[8]; // 编译时报错 能看到这么看似没啥问题的一个简单语句 ...

  9. java范型集合中的成员排序

    范型集合中的类是JsonObject,不是自定义类,如果是自定义类就直接取要比较的字段值. ArrayList<JSONObject> TList = new ArrayList<J ...

随机推荐

  1. C++设计模式:Template Method

    我使用过一个简单的后台服务框架.这个框架上手很容易,我只需要继承一个基类,同时实现,或重写(override)基类声明的几个接口(这些接口声明为虚函数,或者纯虚函数),然后调用基类定义好的run()函 ...

  2. http自动跳转https小记

    因近期跌代更新ios/android后,由于担心ios https从17年起限制的问题,故目前我们将http更改为https,但既然支持https,故想将服务器直接更新为https,将原http请求自 ...

  3. CSS知识点汇总 (全是干货O(∩_∩)O~ )

    一.CSS选择器 [选择器的命名规则] 1.只能有字母数字下划线组成,不能有其他任何特殊字符 2.开头不能是数字 [通用选择器]1.写法:*{}2.作用:选中页面中的所有标签(大范围修改)3.优先级: ...

  4. C#-MVC开发微信应用(2)--OAuth2.0网页授权

    微信公众平台最近新推出微信认证,认证后可以获得高级接口权限,其中一个是OAuth2.0网页授权,很多朋友在使用这个的时候失败了或者无法理解其内容,希望我出个教程详细讲解一下,于是便有了这篇文章. 一. ...

  5. Kubernetes 1.5安装

    Kubernetes从1.3开始引入kubeadm来试图简化其复杂的安装.但kubeadm至今仍不稳定,而且我个人觉得kubeadm反而麻烦,还不如直接用脚本或者其他自动化工具来安装来的利索.关于ku ...

  6. 免费搭建wordpress博客有感

    之前一直有搭建个wordpress博客的想法,但一直没有实施.最近离职之后,空闲时间多了起来,就开始折腾wordpress博客起来. wordpress博客可玩性很高,但刚开始只想练练手,就没有想买域 ...

  7. ORACLE获取表信息方法

    获取表: select table_name from user_tables; //当前用户的表 select table_name from all_tables; //所有用户的表 select ...

  8. 隐性URL与显性URL区别与SEO考虑

    隐性URL与显性URL经常在实现页面跳转的时候用到,这两种方式有什么区别,各自对SEO有什么影响?看一下阿里云的官方文档解释: 显性URL转发: 例如: http://b.com/ 指向 http:/ ...

  9. (21)IO流之对象的序列化和反序列化流ObjectOutputStream和ObjectInputStream

    当创建对象时,程序运行时它就会存在,但是程序停止时,对象也就消失了.但是如果希望对象在程序不运行的情况下仍能存在并保存其信息,将会非常有用,对象将被重建并且拥有与程序上次运行时拥有的信息相同.可以使用 ...

  10. cuda事件的使用

    cudaEvent_t start,stop; cudaEventCreate(&start);//创建事件 cudaEventCreate(&stop); cudaEventReco ...