先看个例子
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<String> strings=Arrays.asList("hehe","","wonking","memeda"); List<Integer> 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 <T,R> List<R> map(List<T> list, Function<T,R> f){ List<R> result=new ArrayList<R>(list.size()); for(T t:list){ result.add(f.apply(t)); } return result; } public static List<Integer> filterOdd(List<Integer> list, Predicate<Integer> p){ List<Integer> result=new ArrayList<>(); for(Integer i: list){ if(p.test(i)){ result.add(i); } } return result; } }
lengths.sort(comparing(Integer::intValue));
这行就用到了方法引用,初看有些晦涩。没关系,我们来从外到内层层剥开它的外衣。
首先看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> T->R
将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<T> & Serializable)
(c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
这段代码中的keyExtractor.apply(c1)进行替换,就变成了这样的代码:
return (Comparator<T> & Serializable)(c1, c2) -> c1.compareTo(c2);
是不是很眼熟?没错,兜兜转转转了一圈又特么回来了,其实中间实际还进行了基本类型装箱拆箱的动作。
准确来说,转换过程是这样滴:
Integer->int->Integer->Comparable
总结下来:这个comparing帮我们做了下面几件事情:
将重复的方法调用进行了折叠,避免重复调用
限制可以比较的类型,通过指定Function的转换目标为Comparable,限制源类型必须实现了Comparable接口才能比较(不过这个理由貌似很牵强)
所谓大道至简,它只是简约,而不简单。也不要惧怕这大道,其背后一定是由诸多简单的规则环环相扣而来,只要一层一层分析下去,真理自会展现。