Java8 新特性总结
Contents
lambda 表达式
java8的最大变化就是引入了lambda表达式,一个紧凑的传递行为的方式。
最简单的Lambda表达式可以用逗号分隔的参数列表->
符号和功能语句块来表示。->
前面的是参数,->
后面的处理的流程。
示例如下:
1 | Arrays.asList( "a", "b", "d" ).forEach( e -> System.out.println( e ) ); |
注意到编译器会根据上下文来推测参数的类型,或者也可以显示地指定参数类型,只需要将类型包在括号里。举个例子:
1 | Arrays.asList( "a", "b", "d" ).forEach( ( String e ) -> System.out.println( e ) ); |
如果Lambda的功能语句块太复杂,我们可以用大括号包起来,跟普通的Java方法一样,如下:
1 | String separator = ","; |
Lambda表达式可能会引用类的成员或者局部变量(会被隐式地转变成final类型),下面两种写法的效果是一样的:
1 | String separator = ","; |
Lambda表达式可能会有返回值,编译器会根据上下文推断返回值的类型。如果lambda的语句块只有一行,不需要return关键字。下面两个写法是等价的:
1 | Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> e1.compareTo( e2 ) ); |
为了让现有功能和lambda 表达式友好兼容,有了函数接口的概念,函数接口是一种只有一个方法的接口。函数接口可以隐式的转换成lambda表达式。
如下定义了一个函数式接口
1 |
|
那么就可以使用Lambda表达式来表示该接口的一个实现.
1 | GreetingService greetService1 = message -> System.out.println("Hello " + message); |
方法引用
方法引用提供了一个很有用的语义来直接访问类或者实例的已经存在的方法或者构造方法。结合Lambda表达式,方法引用使语法结构紧凑简明。不需要复杂的引用。
下面以Car 类做示例说明支持的4种方法引用。
1 | public static class Car { |
第一种方法引用是构造方法引用,语法是:Class::new
,
对于泛型来说语法是:Class<T >::new
,注意构造方法没有参数。
1 | final Car car = Car.create( Car::new ); |
第二种方法引用是静态方法引用,语法是:Class::static_method
注意这个静态方法只支持一个类型为Car的参数。
1 | cars.forEach( Car::collide ); |
第三种方法引用是类实例的方法引用,语法是:Class::method
注意方法没有参数。
1 | cars.forEach( Car::repair ); |
最后一种方法引用是引用特殊类的方法,语法是:instance::method
,注意只接受Car类型的一个参数。
1 | final Car police = Car.create( Car::new ); |
Optional
Optional 是核心类库新设计的一个数据类型,用来替换null值。人们常用null值来表示值不存在,Optional对象可以更好的表达这个概念,使用null表示值最大的问题在于NullPointerException。一旦引用一个存储null值的变量,程序会立即崩溃。
Optional 是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。Optional 类的引入很好的解决空指针异常。
使用工厂方法of,可以从某个值创建出一个Optional对象,Optional对象相当于值的容器,该值可以通过get方法提取。
1 | Optional<String> a = Optional.of("a"); |
Optional 对象也可能为空,因此还有一个对应的工厂方法empty(创建一个空值在Optional中)。另外一个工厂方法ofNullable则可以将一个空值转换为Optional对象。还有一个方法isPresent 表示一个Optional对象里是否有值。
1 | Optinoal emptyOptional = Optional.empty(); |
T orElse(T other)
方法表示如果存在值,则返回值,否则返回other。
1 | Integer value1 = a.orElse(new Integer(0)); |
Stream
Stream API引入了函数式编程。Stream 是用函数式编程方式在集合类上进行复杂操作的工具。首先注意stream()方法的调用,返回了内部迭代中的相应接口:Stream。
例如要计算来自北京的艺术家人数
1 | long count = allArtists.steeam().filter(artist->artist.isForm("Beijing").count()); |
上面分为两步,分别是1找出所有来自北京的艺术家2计算人数。每个操作都对应stream接口的一个方法。为了找出来自北京的,需要对stream对象进行过滤:使用了filter。count()方法计算给定Stream里包含多少个对象。
Stream里的一些方法虽然是普通的Java方法,但返回的Stream对象却不是一个新集合。而是创建新集合的配方。
1 | allArtists.stream().filter(artist->artist.isFrom("Beijing") );//只过滤不计数 |
这行代码并没有做什么实际性的工作,filter只刻画了Stream,但是没有产生新的集合,像filter这样只描述Stream最终不产生新集合的方法叫做惰性求值方法,而像count这样最终会从Stream产生值的方法叫作及早求值方法。
1 | 流的特点: |
在过滤器中加入一条print语句,来输出艺术家的名字,就能看出不同。
1 | allArtiest.stream().filter(artiest->{System.out.println(artiest.getName());}) // 由于使用了惰性求值,没有输出艺术家的名字。 |
如果将同样的输出语句加入一个拥有终止操作的流,如一个计数操作,艺术家的名字就会被输出。
常用的流操作主要有:
collect(toList()) 方法由Stream里的值生成一个列表,是一个及早求值操作。
Stream的of方法使用一组初始值生成新的Stream。
1 | List<String> collected =Stream.of("a","b","c").collect(Collectors.toList()); |
上面是使用collect(toList())方法从Stream中生成一个列表。由于很多Stream操作都是惰性操作,因此调用Stream上一系列方法后,还需要最后再调用一个类似collect的及早求值方法。
map 函数可以将一种类型的值转换成另外一种新的类型。
filter用来遍历元素并检查其中的元素。
flatmap 可以用Stream替换值,然后将多个Stream连接成一个Stream.
Author: corn1ng
Link: https://corn1ng.github.io/2019/09/09/java8总结/
License: 知识共享署名-非商业性使用 4.0 国际许可协议