Java8应该是目前最大的一次更新了,更新后咱们迎来了不少新特性,其中便包括Lambda表达式,函数式编程的思想正式进入Java,让咱们看一个经典案例。java
采用匿名内部类已经算简介了,若是专门用一个类去实现Comparator再new出来就更烦了,过去的写法:express
// 已经建立好了三个Person实例 List<Person> people = Arrays.asList(person1, person2, person3); Collections.sort(people, new Comparator<Person>() { @Override public int compare(Person o1, Person o2) { return o1.getAge().compareTo(o2.getAge()); } });
Lambda版本写法:编程
Collections.sort(people, (p1, p2) -> p1.getAge().compareTo(p2.getAge()));
还有更简洁的方法引用写法:设计模式
Collections.sort(people, Comparator.comparing(Person::getAge));
是否是真的短真的易读,语法糖真的甜!已经9102年了,函数式编程被提到的愈来愈多,深谙照猫画虎已经行不通了,并且函数式编程和设计模式的碰撞也不少,真的有必要了解下相关概念app
函数式编程是一种思想,核心是行为参数化,把一段代码像值同样传递给方法,传入不一样的代码实现不一样的功能ide
这是否是很像策略模式以及模板模式?如例1所示,不须要大量的套路代码了,也不须要把代码写到一个类中而后新建实例对象最后把实例对象传递函数式编程
函数式接口就是只定义一个抽象方法的接口来表示行为,抽象方法不容许抛出受检异常,Java8接口能够有default方法了,函数式接口是容许有default方法的函数
Lambda表达式看上去确实颇有吸引力,我能在任何地方都使用么?答案是不能的,咱们只能经过Lambda表达式把代码传到函数式接口中,拿例1中的Comparator接口来看设计
@FunctionalInterface public interface Comparator<T> { int compare(T o1, T o2); }
Comparator接口只有compare一个抽象方法,Java8特地给它加了注解告诉咱们这就是个函数式接口,其实也很好理解,咱们没有建立任何实例,只传了一串代码,若是Comparator有两个抽象方法,编译器怎么知道咱们实现的是compare方法呢code
上面都是说的函数式编程,那么什么是Lambda
Lambda就是匿名的行为参数化的一种语法实现,它没有名称,但它有参数列表、函数主体、返回类型,可能还有一个能够抛出的异常列表
tips:若是statements很长,那么咱们就不该该用Lambda,而应该单独实现一个方法,而后使用方法引用这样可读性更好,继续例1
// 好比说在MyUtils类下写个方法,故意加长 public static Integer sortPersonByName(Person person1, Person person2) { Integer age1 = person1.getAge(); Integer age2 = person2.getAge(); return age1.compareTo(age2); } // 又用到了方法引用,咱们能够把方法引用看成一种便于阅读的语法糖,功能也是传递代码 Collections.sort(people, MyUtils::sortPersonByAge);
在使用Lambda的时候咱们没有任何类型声明就能工做这是怎么作到的呢?
函数式接口的抽象方法的签名基本上就是Lambda表达式的签名。咱们将这种抽象方法叫做
函数描述符
Comparator.compare的签名
int compare(T o1, T o2);
这个函数式接口的签名就能够描述成须要两个相同类型的变量,而后返回int
( T, T ) -> int
Lambda的签名
(p1, p2) -> p1.getAge().compareTo(p2.getAge()) // 这是易读的写法,咱们也能够写成方便说明 (Person p1, Person p2) -> p1.getAge().compareTo(p2.getAge())
能够描述成须要两个Person变量,而后compareTo方法返回int
(Person, Person) -> int
编译器会作类型推断和类型检查,发现两个签名匹配,咱们的Lambda表达式就能够顺利执行了
tips:Lambda主体是语句表达式的时候(parameters) -> expression ,尽管expression返回可能不是void,可是也是兼容 T -> void 签名的
除了 Runnable,Comparator等经常使用函数式接口,为了推进函数式编程,Java8又在 java.util.function 包下为咱们提供了大量好用的函数式,由于基本数据类型不能抽象成对象,因此能够看到有大量Double,Int,Long前缀的接口,咱们只看比较核心的:
接口名 | 抽象方法 | 描述符 |
---|---|---|
Predicate
|
boolean test(T t) | T -> boolean |
BiPredicate<T, U> | test(T t, U u) | ( T, U ) -> boolean |
Consumer
|
void accept(T t) | T -> void |
BiConsumer<T, U> | void accept(T t, U u) | ( T, U ) -> void |
Function<T, R> | R apply(T t) | T -> R |
BiFunction<T, U, R> | R apply(T t, U u) | ( T, U ) -> R |
Supplier
|
T get() | void -> T |
tips:Predicate这种对给定内容作判断返回boolean 值,咱们叫作谓词
使人惊喜的是,java8提供的函数式接口还有许多好用的default方法,可让咱们把多个Lambda复合起来,组成流水线。拿用的比较多的Function接口举例,咱们要写一封邮件,咱们关注的是信的内容
Function<String, String> writeEmailHeader = text -> "Hi ," + "\n" + text; Function<String, String> writeEmailText = text -> text + "\n"; Function<String, String> writeEmailFooter = text -> text + "BRs" + "\n" + "Chen"; Function<String, String> writeEmail = writeEmailHeader.andThen(writeEmailText).andThen(writeEmailFooter); System.out.println(writeEmail.apply("I will take half day sick leave today")); /** * 输入以下: * Hi , * I will take half day sick leave today * BRs * Chen */