先看个例子
import java.util.ArrayList;
import java.util.Arrays;
import static java.util.Comparator.comparing;
import java.util.Comparator;
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
/**
* Created by kewangk on 2018/1/29.
*/
public class TestJava8 {
public static void main(String[] args) {
List strings=Arrays.asList("hehe","","wonking","memeda");
List lengths=map(strings, (String s)->s.length());
System.out.println(lengths);
lengths.sort(comparing(Integer::intValue));
lengths.sort((i1,i2)-> i1.compareTo(i2));
System.out.println(lengths);
}
public static List map(List list, Function f){
List result=new ArrayList(list.size());
for(T t:list){
result.add(f.apply(t));
}
return result;
}
public static List filterOdd(List list, Predicate p){
List result=new ArrayList<>();
for(Integer i: list){
if(p.test(i)){
result.add(i);
}
}
return result;
}
}
这行就用到了方法引用,初看有些晦涩。没关系,我们来从外到内层层剥开它的外衣。
首先看List.sort(Comparator super E> c)这个方法
sort方法需要传进一个Comparator,这是一个函数式接口,所以我们首先想到能用lambda表达式来传参
lengths.sort(( i1, i2) -> i1.compareTo(i2));
所以你像上面这样写没错,但编译器会抱怨一句
Inspection looks for Comparators defined as lambda expressions which could be expressed using methods like Comparator.comparing()
Can be replaced with Comparator.naturalOrder
这句话的意思是说,语法检查器查找定义为lambda表达式的Comparator,可以使用Comparator.comparing()方法来代替,并且还心细到给你找了一个现成的自然顺序比较器给你开箱即用(我们先忽略这个)。
不明白这句话的意思?这其实就是在鼓励你是用lambda表达式。
你就奇怪了,我不是已经用了lambda表达式嘛?干嘛还非要我换别的用法?
我们知道,lambda表达式其实就是提供了某个函数式接口的一个实现,只不过它本身不携带自己实现的哪个接口的信息,而是由编译器进行类型推断。
既然lambda表达式本身不携带实现接口的信息,那么我们需要知道(一眼就看出来的那种,弯都不带拐的)某个方法到底需要哪种类型的lambda表达式怎么办呢?
所以Java API的设计师们帮我们设计出这样一个静态方法
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)); }
从这个方法的实现来看,我们可以看出设计师们设计此方法的更多用意:
为了兼容老接口Comparable,去看看API可以知道,Comparable接口1.2版本开始出现
为了实现方法引用,那么方法引用又是为了实现什么?现在你还不能理解,接着看下去
为了更优雅的代码
为了爱
为了和平
个中奥妙,真无穷无尽也
再来看看这个comparing方法做了什么
返回一个Comparator(正是sort方法需要的),传入一个Function,另一个函数式接口,来看看Function的函数描述符(再次感受一下lambda表达式的简洁优美)
Function
将T变成R,OOP式的表达就是,将一种对象转化成另一种对象,这就是Function的全部
具体到这个例子中来,看看Function实现了从什么到什么的转化
Function super T, ? extends U>,这个泛型限定的是,源对象是T或T的一个父类,目标对象是U或U的一个子类。T没有更具体的限制,那么来看U,U是实现了Comparable接口的一个对象
现在清楚了,Function要做的事情就是,将T转化成一个Comparable对象,T不管它是什么类型,只要它实现了Comparable接口,这个转换就会成功
所以这个Function的作用用一句话总结来说就是,将实现了Comparable接口的两个对象转化成Comparable对象进行比较,整个比较逻辑,又是作为一个lambda表达式形式的Compatator接口进行返回
至此,最开始的那行代码已经被我们剥得只剩裤衩了。感觉是不是很美妙~~
再来看看Integer::intValue
可以肯定,这是一个lambda表达式形式的Function,那么它代表前面说的函数描述符中T->R的哪一个呢?
很显然,它代表T,之前我们分析Function的时候已经确定,R就是Comparable,但是一直没说T从哪来。
专业来讲,它是一个方法引用,即引用Integer中的intValue方法。
与这个引用等价的lambda表达式是:(Integer i)->i.intValue()
这个lambda表达式的签名不正是与Function的apply()方法签名一致吗
如果我们把这个lambda对
return (Comparator
(c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
这段代码中的keyExtractor.apply(c1)进行替换,就变成了这样的代码:
return (Comparator
是不是很眼熟?没错,兜兜转转转了一圈又特么回来了,其实中间实际还进行了基本类型装箱拆箱的动作。
准确来说,转换过程是这样滴:
Integer->int->Integer->Comparable
总结下来:这个comparing帮我们做了下面几件事情:
将重复的方法调用进行了折叠,避免重复调用
限制可以比较的类型,通过指定Function的转换目标为Comparable,限制源类型必须实现了Comparable接口才能比较(不过这个理由貌似很牵强)
所谓大道至简,它只是简约,而不简单。也不要惧怕这大道,其背后一定是由诸多简单的规则环环相扣而来,只要一层一层分析下去,真理自会展现。