在C++、Python等语言里都有Lambda表达式,Java 8也新增了这一特性。html
在java给变量赋值是这样的:java
int num=123; String str="hello world!"; Boolean flag=str.startsWith("h");
若是你想把“一块代码”赋给一个Java变量,应该怎么作呢?api
好比,我想把右边那块代码,赋给一个叫作aBlockOfCode的Java变量数组
aBlockOfCode=public void doSomeShit(String s){ System.out.println(s); }
在Java 8以前,这个是作不到的。可是Java 8问世以后,利用Lambda特性,就能够作到了,下面咱们一步步简化oracle
这样,咱们就成功的很是优雅的把“一块代码”赋给了一个变量。而“这块代码”,或者说“这个被赋给一个变量的函数”,就是一个Lambda表达式。ide
可是这里仍然有一个问题,就是变量aBlockOfCode的类型应该是什么?函数
在Java 8里面,全部的Lambda的类型都是一个接口,而Lambda表达式自己,也就是”那段代码“,须要是这个接口的实现。这是我认为理解Lambda的一个关键所在,简而言之就是,Lambda表达式自己就是一个接口的实现。直接这样说可能仍是有点让人困扰,咱们继续看看例子。咱们给上面的aBlockOfCode加上一个类型:oop
这种只有一个接口函数须要被实现的接口类型,咱们叫它”函数式接口“。为了不后来的人在这个接口中增长接口函数致使其有多个接口函数须要被实现,变成"非函数接口”,咱们能够在这个上面加上一个声明@FunctionalInterface, 这样别人就没法在里面添加新的接口函数了:优化
@FunctionalInterface public interface MyLambdaInterface { void doSomeShit(String s); }
这样,咱们就获得了一个完整的Lambda表达式声明:ui
public void test(){ MyLambdaInterface aBlockOfCode= (s) -> System.out.println(s); }
在Java 7以前都是这样接口的实现是这样
public class MyInterfaceImpl implements MyLambdaInterface{ @Override public void doSomeShit(String s) { System.out.println(s); } }
这两种写法本质上是等价的。可是显然,Java 8中的写法更加优雅简洁。而且,因为Lambda能够直接赋值给一个变量,咱们就能够直接把Lambda做为参数传给函数, 而传统的Java必须有明确的接口实现的定义,初始化才行
public static void enact(MyLambdaInterface myLambda,String s){ myLambda.doSomeShit(s); }
public static void main(String[] args) { enact(s -> System.out.println(s) ,"myLambda"); System.out.println("执行"); }
执行结果:
myLambda 执行
假设Person的定义和List<Person>的值都给定
public class Person { private String firstName; private String lastName; private int age; public Person(String lastName, String firstName,int age) { this.lastName = lastName; this.firstName = firstName; this.age = age; } ...省略getset }
List<Person> guiltyPersons=Arrays.asList( new Person("Yixing", "Zhao",25), new Person("Yanggui", "Li",30 ), new Person("Chao", "Ma", 29) );
如今须要你打印出guiltyPersons List里面全部LastName以"Z"开头的人的FirstName。
原生态Lambda写法:定义两个函数式接口,定义一个静态函数,调用静态函数并给参数赋值Lambda表达式。
@FunctionalInterface public interface NameChecker { boolean check(Person p); }
@FunctionalInterface public interface Executor { void execute(Person p); }
public static void checkAndExecute(List<Person> personList,NameChecker nameChecker,Executor executor ){ for (Person person : personList) { if(nameChecker.check(person)){ executor.execute(person); } } }
public class Test { public static void main(String[] args) { enact(s -> System.out.println(s) ,"myLambda"); System.out.println("执行"); List<Person> guiltyPersons=Arrays.asList( new Person("Yixing", "Zhao",25), new Person("Yanggui", "Li",30 ), new Person("Chao", "Ma", 29) ); checkAndExecute(guiltyPersons,p -> p.getFirstName().startsWith("Z") , p -> System.out.println(p.getFirstName())); }
执行结果:
myLambda 执行 Zhao
这个代码实际上已经比较简洁了,可是咱们还能够更简洁么?
固然能够。在Java 8中有一个函数式接口的包,里面定义了大量可能用到的函数式接口(java.util.function (Java Platform SE 8 ))。因此,咱们在这里压根都不须要定义NameChecker和Executor这两个函数式接口,直接用Java 8函数式接口包里的Predicate<T>和Consumer<T>就能够了——由于他们这一对的接口定义和NameChecker/Executor实际上是同样的。
@FunctionalInterface public interface Predicate<T> { boolean test(T t); }
@FunctionalInterface public interface Consumer<T> { void accept(T t); }
第一步简化 - 利用函数式接口包:
public static void checkAndExecute(List<Person> personList,Predicate<Person> predicate,Consumer<Person> consumer){ for (Person person : personList) { if(predicate.test(person)){ consumer.accept(person); } } }
checkAndExecute(guiltyPersons,p -> p.getFirstName().startsWith("Z") , p -> System.out.println(p.getFirstName()));
静态函数里面的for each循环实际上是很是碍眼的。这里能够利用Iterable自带的forEach()来替代。forEach()自己能够接受一个Consumer<T> 参数。
第二步简化 - 用Iterable.forEach()取代foreach loop
public static void checkAndExecute(List<Person> personList,Predicate<Person> predicate,Consumer<Person> consumer){ personList.forEach(p -> {if(predicate.test(p)) consumer.accept(p);}); }
checkAndExecute(guiltyPersons,p -> p.getFirstName().startsWith("Z") , p -> System.out.println(p.getFirstName()));
因为静态函数其实只是对List进行了一通操做,这里咱们能够甩掉静态函数,直接使用stream()特性来完成。stream()的几个方法都是接受Predicate<T>,Consumer<T>等参数的(java.util.stream (Java Platform SE 8 ))。
这里解释下stream
Stream(流)是一个来自数据源的元素队列并支持聚合操做
和之前的Collection操做不一样, Stream操做还有两个基础的特征:
第三步简化 - 利用stream()替代静态函数
public static void checkAndExecute(List<Person> personList,Predicate<Person> predicate,Consumer<Person> consumer){ personList.stream() .filter(p -> p.getFirstName().startsWith("Z")) .forEach(p -> System.out.println(p.getFirstName())); }
对比最开始的Lambda写法,这里已经很是很是简洁了。可是若是,咱们要求变一下,变成print这我的的所有信息,及p -> System.out.println(p); 那么还能够利用方法引用经过方法的名字来指向一个方法。
方法引用可使语言的构造更紧凑简洁,减小冗余代码。
方法引用使用一对冒号 ::
Java 中 4 种不一样方法的引用
构造器引用:它的语法是Class::new,或者更通常的Class< T >::new
静态方法引用:它的语法是Class::static_method
特定类的任意对象的方法引用:它的语法是Class::method
特定对象的方法引用:它的语法是instance::method
例如:
public static void main(String[] args) { List names = new ArrayList(); names.add("Google"); names.add("Taobao"); names.add("Baidu"); names.add("Sina"); names.forEach(System.out::println); }
实例中咱们将 System.out::println 方法做为静态方法来引用
第四步简化 - 若是是println(p),则能够利用方法的引用代替forEach中的Lambda表达式
personList.stream().filter(p -> p.getFirstName().startsWith("Z")).forEach(System.out::println)
Lambda配合Optional<T>可使Java对于null的处理变的异常优雅
Optional 类是一个能够为null的容器对象。若是值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
Optional 是个容器:它能够保存类型T的值,或者仅仅保存null。Optional提供不少有用的方法,这样咱们就不用显式进行空值检测。
Optional 类的引入很好的解决空指针异常。
例如:假设咱们有一个person object,对这个对象进行null判断,Java 7都是以下:
private static final Person UNKNOWN_PERSON=null; public static Person testOptional(){ Person person = null; if(person != null){ return person; }else{ return UNKNOWN_PERSON; } }
Java 8 则使用Optional 特性,具体以下:
private static final Person UNKNOWN_PERSON=null; public static Person testOptional(){ Person person = null; // Optional.ofNullable - 容许传递为 null 参数 Optional<Person> personOpt=Optional.ofNullable(person); // if(personOpt.isPresent()){ // return personOpt.get(); // }else{ // return UNKNOWN_PERSON; // } //最后优化为最简化的 personOpt.ifPresent(System.out::println); return personOpt.orElse(UNKNOWN_PERSON); }
Java 8 的新特性先介绍到这里,与你们共同进步
文章参考:https://www.zhihu.com/question/20125256/answer/324121308