Java 8其他新特性
1. Lambda表达式
1.1 介绍
Lambda是一个匿名函数,使用它可以写出更简洁、更灵活的代码。Java中lambda表达式的本质是作为函数式接口的实例。 在函数式编程语言中,lambda表达式的类型时函数,而在Java中,lambda表达式是对象,它们必须依附于一类特别的对象类型:函数式接口。
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("r1");
}
};
r1.run();
Runnable r2 = () -> {
System.out.println("r2");
};
r2.run();
1.2 格式
- ->:lambda操作符,又称箭头操作符
- ->左边:lambda形参列表,存放接口中抽象方法的形参列表
- ->右边:lambda体,重写抽象方法的方法体
1.3 使用
- lambda表达式可以有多个形参,可以有多条执行语句,可以有返回值
- lambda形参列表的数据类型可以省略,称为类型推断
- lambda形参列表只需要一个参数时,参数的小括号可以省略
- lambda体只有一条语句时,return和大括号都可以省略
2. 函数式接口
2.1 定义
如果一个接口中,只声明了一个抽象方法,称为函数式接口。可以通过lambda表达式来创建函数式接口的对象。可以在一个接口上使用@FunctionalInterface注解,从而检验该接口是否是一个函数式接口。Java 8在java.util.function包下定义了丰富的函数式接口。
2.2 Java内置函数式接口
Java内置四大核心函数式接口:
函数式接口 | 参数类型 | 返回类型 | 用途 |
---|---|---|---|
Consumer< T >消费型接口 | T | void | 对类型为T的对象应用操作,void accept(T t) |
Supplier< T >供给型接口 | 无 | T | 返回类型为T的对象,T get() |
Function< T,R >函数型接口 | T | R | 对类型为T的对象应用操作,并返回结果:R apply(T t) |
Predicate< T >断定型接口 | T | boolean | 确定类型为T的对象是否满足某约束,并返回boolean值:boolean test(T t) |
3. 方法引用与构造器引用
3.1 方法引用
- 方法引用可以看做是lambda表达式深层次的表达,可以认为是lambda表达式的一个语法糖
- 使用情境:当要传递给lambda体的操作,已经有实现的方法了,可以使用方法引用
- 要求:实现接口的抽象方法的参数列表和返回值类型,必须与方法引用的方法的参数列表和返回值类型保持一致(第3种格式不满足该要求)
- 格式:使用操作符“::”将类(对象)与方法名分隔开
对象 :: 非静态方法
类名/对象 :: 静态方法
类名 :: 非静态方法(实现接口的抽象方法的返回值类型与方法引用的方法一致,参数列表比方法引用的参数多一个,调用方法引用的对象作为第一个参数,左边的类写该对象所在的类)
3.2 构造器引用
- 要求:函数式接口的抽象方法的形参列表和构造器的形参列表一致,抽象方法的返回值类型即为构造器所属类的类型
- 格式:类名 :: new
3.3 数组引用
- 要求:把数组看作一个特殊的类,其他操作与构造器引用相同
- 格式:String[] :: new
4. Stream API
4.1 介绍
4.1.1 概述
Stream API(java.util.stream)把真正的函数式编程风格引入到Java中,提供了一种高效易用的处理数据的方式。使用Stream API对集合数据进行操作,就类似于使用SQL进行数据库查询。
4.1.2 特性
- Stream自己不会存储元素
- Stream不会改变源对象,操作会返回一个持有结果的新Stream
- Stream操作时延迟执行的,需要结果时才会执行
4.1.3 步骤
- 创建Stream对象:通过一个数据源(集合、数组),获取一个流
- 中间操作:一个中间操作链,对数据源的数据进行处理
- 终止操作(终端操作):一旦执行终止操作,就执行中间操作链,并产生结果,之后不会再被使用
4.2 创建Stream对象
4.2.1 通过集合创建
Java8中的Collection接口被扩展,提供了两个获取流的方法:
- default Stream<E> stream():返回一个顺序流
- default Stream<E> parallelStream():返回一个并行流
4.2.2 通过数组创建
Java8中的Arrays的静态方法stream()可以获取数组流:
- static <T> Stream<T> stream(T[] array):返回一个流
4.2.3 通过Stream的of()方法创建
Stream的静态方法of()通过显示值创建一个流,可以接收任意数量的参数:
- public static<T> Stream<T> of(T… values):返回一个流
4.2.4 创建无限流
使用静态方法Stream.iterate()和Stream.generate()可以创建无限流:
- public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f):迭代
- public static<T> Stream<T> generate(Supplier<T> s):生成
4.3 中间操作
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何处理。在终止操作时一次性全部处理,称为“惰性求值”。
4.3.1 筛选与切片
- filter(Predicate p):接受lambda,从流中排除某些元素
- distinct():筛选,通过流所生成元素hashCode()和euqals()去除重复元素
- limit(long maxSize):截断流,使其元素不超过给定数量
- skip(long n):跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("AAA");
arrayList.add("ABB");
arrayList.add("BBA");
Stream<String> stream = arrayList.stream();
stream.filter(s -> s.substring(0, 1).equals("A")).forEach(System.out::println);
4.3.2 映射
- map(Function f):接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
- flatMap(Function f):接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
List<String> list = Arrays.asList("aa", "bb", "cc", "dd");
list.stream().map(str -> str.toUpperCase()).forEach(System.out::println);
这两个方法类似于list.add(list)和list.addAll(list)的区别
4.3.3 排序
- sorted():产生一个新流,其中按自然顺序排序
- sorted(Comparator com):产生一个新流,其中按比较器顺序排序
List<Integer> list = Arrays.asList(12, 45, 34, 75, 3, 26);
list.stream().sorted().forEach(System.out::println);
4.4 终止操作
终止操作会从流水线生成结果,其结果可以是任何不是流的值。流进行了终止操作后,不能再次使用。
4.4.1 匹配与查找
- allMatch(Predicate p):检查是否匹配所有元素
- anyMatch(Predicate p):检查是否至少匹配一个元素
- noneMatch(Predicate p):检查是否没有匹配所有元素
- findFirst():返回第一个元素
- findAny():返回当前流的任意元素
- count():返回流中元素总数
- max(Comparator c):返回流中最大值
- min(Comparator c):返回流中最小值
- forEach(Consumer c):内部迭代(iterator称为外部迭代)
4.4.2 归约
- reduce(T iden, BinaryOperator b):将流中元素反复结合起来,得到一个值,返回T
- reduce(BinaryOperator b):将流中元素反复结合起来,得到一个值,返回Optional<T>
List<Integer> list = Arrays.asList(12, 45, 34, 75, 3, 26);
Integer reduce = list.stream().reduce(0, Integer::sum);
System.out.println(reduce);
4.4.3 收集
- collect(Collector c):将流转换为其他形式,接收一个Collector接口的实现,用于给Stream中元素做汇总的方法
Collectors类中提供了很多静态方法,可以方便创建常见的收集器实例:
- toList():把流中元素收集到List
- toSet():把流中元素收集到Set
- toCollection():把流中元素收集到创建的集合
List<Integer> list = Arrays.asList(12, 45, 34, 75, 3, 26);
List<Integer> collect = list.stream().filter(i -> i > 20).collect(Collectors.toList());
System.out.println(collect);
5. Optional类
5.1 介绍
为了解决空指针异常,Google公司引入了Optional类。Optional<T>类(java.util.Optional)是一个容器类,它可以保存类型T的值,代表这个值存在。或者仅仅保存null,代表这个值不存在。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
5.2 方法
5.2.1 创建Optional类对象
- Optional.of(T t):创建一个Optional实例,t必须非空
- Optional.empty():创建一个空的Optional实例
- Optional.ofNullable(T t):t可以为null
5.2.2 判断Optional容器中是否包含对象
- boolean isPresent():判断是否包含对象
- void ifPresent(Consumer<? super T> consumer):如果有值,就执行Consumer接口的实现代码,并且该值会作为参数传给它
5.2.3 获取Optional容器的对象
- T get():如果调用对象包含值,返回该值,否则抛异常
- T orElse(T other):如果有值则将其返回,否则返回指定的other对象
- T orElseGet(Supplier<? extends T> other):如果有值则将其返回,否则返回由Supplier接口实现提供的对象
- T orElseThrow(Supplier<? extends X> exceptionSupplier):如果有值则将其返回,否则抛出由Supplier接口实现提供的异常
String str = "hello";
Optional<String> optional1 = Optional.of(str);
String s = optional1.get();
System.out.println(s);
str = null;
Optional<String> optional2 = Optional.ofNullable(str);
String s1 = optional2.orElse("hi");
System.out.println(s1);