环境
Java:1.8+
前言
今天发了一个问题:
问题描述:
- 对
List
进行排序,如果在Comparator.comparing()
方法中,只使用一个字段排序,没有问题。 - 但是如果使用多字段排序,并且写法是
lambda
时,就会有问题。 - 如果使用方法引用可以看到不会报错。
解决
为什么, 下面这种写法会有问题呢?
dcList.sort(Comparator.comparing((p)-> p.getRow()).thenComparing(n-> n.getCol()));
dcList.sort(Comparator.comparing((p)-> p.getRow()).thenComparing(DmnCell::getCol));
也就是说lambda表达式
的写法有问题。可是Comparator.comparing()
方法,本质上就是传lambda表达式
啊。
把鼠标放到报错处,会提示:
Cannot resolve method'getRow' in'Object'
也就是说,p的类型为Object。这说明,p的引用类型没有自动推断出来。要解决这个问题,很简单,手动指定下就可以啦
可以看到,手动指定类型后,后面的报错也一并解决了。
探究原因
//正常
dcList.sort(Comparator.comparing((p)-> p.getRow()));//不正常;这种写法会报错
dcList.sort(Comparator.comparing((p)-> p.getRow()).thenComparing(n-> n.getCol()));
为什么这种lambda
表达式,推断不出类型呢?
首先,我们知道如果没有thenComparing()
方法,那么编译器,根据上下文,其实这里也就只有上文dcList
可知,p的类型应该是DmnCell
。
但是,后面有了thenComparing()
方法之后,编译器根据上文信息可知可能是DmnCell类型,但是却不知下文(thenComparing()
)实际会返回什么类型;(下文输入类型可能知道可能不知道);所以这里,在comparing
方法这里,返回的目标类型推断失败,失败的类型默认就是Object
;
也就是我们可以得出,lambda
表达式类型推断,需要明确上文信息和下文的信息。具体点,上文会传什么类型过来,下文会返回什么类型。只有上下文都明确的地方,目标类型才会推断出来。(以上是个人猜想,并没有什么权威断言)。
再举个例子
//没问题,可以推断出来publicstaticComparator<CustUser>createUser1(){return(CustUser user1,CustUser user2)-> user1.getAge()- user2.getAge();}//没问题,可以推断出来publicstaticComparator<CustUser>createUser2(){return(user1, user2)-> user1.getAge()- user2.getAge();}
如果我们将上面的return语句拆分成两条,会出现问题问题呢?
//有问题,不可以推断出来Comparator comparator=(user1, user2)-> user1.getAge()- user2.getAge();
这时候就会编译报错,说找不到getAge方法。这是因为我们返回的Comparator
并没有指明类型,所以默认情况下是Object类型。Object类型并没有getAge方法,所以报错。
我们可以这样改写:
//没问题,可以推断出来Comparator<CustUser> comparator=(user1, user2)-> user1.getAge()- user2.getAge();
总结
当写lambda
时,如果遇到原本可以正常写,但是又突然不能写或写了就报错的情况,很可能就是没有明确目标类型,此时,只需要明确指定目标类型即可。
目标类型:Lambda
表达式的类型,也称为 目标类型。