java 8新特性--函数式编程
最近实习过程中发现业务代码的编写多用Stream流,Optional类等等,发现这是Java 8中的新特性,对于集合类的操作非常便捷,于是抽空学习了一下Java 8 中的新特性。
Java8
- 修改原因:
抽象级别不够,面对大型数据集合操作,处理复杂,并行低效。
- 变化:
-
- 集合类API的增加;
- Java 8中的另一个变化是引入了默认方法和接口的静态方法,它改变了人们认识类库的方式,接口中的方法也可以包含代码体了。
lambda表达式
lambda表达式将行为像数据一样进行传递。使用操作符->,左边为参数(有时可以显式地声明参数类型),右边为执行的操作。
函数式接口
- 定义
函数式接口本质上就是一个接口,里面只有一个抽象方法,用作 Lambda 表达式的类型。Java8中新增了java.util.function接口,函数式接口广泛用在支持lambda表达式的API中。这些接口有一个抽象方法,会被lambda表达式的定义所覆盖。
- 如果在某个接口上声明了@FunctionalInterface注解,那么编译器就会按照函数式接口的的定义来要求该接口。
- 如果一个接口只有一个抽象方法,不管有多少个默认方法,有没有@FunctionalInterface注解,都是函数式接口。
- java.util.function中核心函数式接口
|
接口 |
参数 |
返回值 |
主要方法 |
|
Predicate<T> |
T |
boolean |
boolean test(T t) |
|
Consumer<T> |
T |
void |
void accept(T t) |
|
Function<T,R> |
T |
R |
R apply(T t) |
|
Supplier<T> |
None |
T |
T get() |
- 引用/实现函数式接口的方法
- lambda表达式
- 方法引用
Java 方法引用是Java 8随着Lambda表达式引入的新特性。其使用条件是:Lambda表达式的主体仅包含一个表达式,且Lambda表达式只调用了一个已经存在的方法;被引用的方法的参数列表和返回值与Lambda表达式的输入输出一致。
-
- 类名::静态方法名,例如Math::abs
- 引用名(对象名)::实例方法名,例如this::equals
- 类名::实例方法名,例如String::concat
- 构造方法引用实现对应的实例
-
- 类名::new
类库
1 public interface Iterable<T> { 2 3 Iterator<T> iterator(); 4 5 default void forEach(Consumer<? super T> action) { 6 Objects.requireNonNull(action); 7 for (T t : this) { 8 action.accept(t); 9 } 10 } 11 ... 12 }
- 默认方法
Java8对接口进行了增强,接口中也可以有具体的方法实现了,不过必须要在方法之前加上修饰符default称为default method即默认方法。
- default 关键字只能在接口中使用(以及用在 switch 语句的 default 分支),不能用在抽象类中。
- 和抽象方法的区别:抽象方法,接口的子类需要实现;默认方法,接口的子类不需要实现,可以直接使用。
- 方法不能够重写(覆盖) Object 中的方法,却可以重载Object 中的方法。eg:toString、equals、 hashCode 不能在接口中被覆盖,却可以被重载。
- 多重继承下的三定律:
-
- 类胜于接口。如果在继承链中有方法体或抽象的方法声明,那么就可以忽略接口中定义的方法。
- 子类胜于父类。如果一个接口继承了另一个接口,且两个接口都定义了一个默认方法,那么子类中定义的方法胜出。
- 没有规则三。如果上面两条规则不适用,子类要么需要实现该方法,要么将该方法声明为抽象方法
- 静态方法
在接口中的方法签名前加上了 static 关键字。
- Optional类
Optional 类(java.util.Optional) 是一个容器类,是为核心类库新设计的一个数据类型,用来替换 null 值。其API中的常用方法有:
- Optional.ofNullable(T t):若 t 不为 null,创建 Optional 实例,否则创建空实例
- isPresent():判断是否包含值
- get():返回该对象,有可能返回 null
- orElse(T t):如果调用对象包含值,返回该值,否则返回t
- orElseGet(Supplier s):如果调用对象包含值,返回该值,否则返回 s 获取的值。 Supplier接口就一个get方法,无入参,出参要和Optional的对象同类型。
Stream流
Stream流和 java.io 包下的 InputStream 和 OutputStream 其实没有关系,他是Java8中新增的部分,极大的便捷了对于集合的遍历操作。集合主要是数据,而流主要是计算。
使用stream流的优势在于它仅暴露一个Stream接口,很好地封装了内部的数据结构,用户在实际操作中无论如何使用都不会影响内部的List或Set。
创建流
- 数组:Arrays.stream()或者Stream.of();
- 集合Collection:.stream()方法
stream流的中间操作
- filter()
从流中过滤出我们需要的数据流
1 //找出数字开头的字符串 2 List<String> beginningWithNumbers = new ArrayList<>(); 3 for(String value : asList("a", "1abc", "abc1")) { 4 if (isDigit(value.charAt(0))) { 5 beginningWithNumbers.add(value); 6 } 7 } 8 9 //转化为如下,过滤后值为TRUE的元素保留,转化为list 10 List<String> beginningWithNumbers 11 = Stream.of("a", "1abc", "abc1") 12 .filter(value -> isDigit(value.charAt(0))) 13 .collect(toList());
- map()
映射,通过函数操作把一个流中的元素转化成新的流中的元素
参数/返回值:接收的是一个 Function类型的参数(Java 8 新增的一个函数式接口,接受一个输入参数 T,返回一个结果 R,Function 接口是只包含一个参数的普通函数接口)
- flatMap()
假如你的集合流中包含子集合,那么使用flatMap可以返回该子集合的集合流,也就是说返回集合中元素铺展开形成的流。
flatMap/map区别:好比说现在有两个班的学生,每个班10人,map()返回值为两个班的集合,而flatmap()返回值为20个学生的集合。
- distinct()
去重,返回无重复元素的结果。
- min()/max()
求最值,只需要传递一个Comparator的comparing()方法即可,就可以进行比较,从而获取最大和最小值。
1 //求这组数中最大值 2 Integer maxNumber = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9) 3 .max(Comparator.comparing(Integer::valueOf)) 4 .get();
- reduce()
reduce() 方法的主要作用是把 Stream 中的元素组合起来,从一组值生成一个值,它有两种用法:
-
- Optional<T> reduce(BinaryOperator<T> accumulator)
没有初始值,只有一个参数,就是运算规则,此时返回 Optional。运算规则可以是 Lambda 表达式(比如 (a, b) -> a + b),也可以是类名::方法名(比如 Integer::sum)。
-
- T reduce(T identity, BinaryOperator<T> accumulator)
有初始值,有运算规则,两个参数,此时返回的类型和起始值类型一致。
转换流
- collect()
Reference
[1]Richard Warburton, 王群锋. Java 8函数式编程. 人民邮电出版社, 2015.
[2]Raoul-Gabriel Urma, Mario Fusco, Alan Mycroft. Java 8实战.
浙公网安备 33010602011771号