前言
本文涉及以下知识:
- Lambda表达式基本概念
- Lambda表达式语法
- Lambda表达式实战:java.util.function
- Stream 流的概念
- Stream实战
Lambda基本概念
Java8的一个新特性就是引入了函数式接口。通常我们的方法的入参都是基本数据类型,或者是实例对象的引用,这在一定程度上限制了方法的多样性,因此Java引入了函数式编程,即在声明(构造器或者方法)时把一个函数接口当成入参。一般这样的入参在实际使用时,会用一个实现了该接口的类对象来传入,这种对象我们一般会写成匿名的,如下所示(声明一个线程):
1 | // 接口Runnable作为声明时的入参,在使用时使用一个匿名类来传入 |
这种匿名函数写起来很笨重,因此引入了Lambda表达式来简化匿名函数的定义,如果用Lambda表达式来声明上述td
对象,则可以写成如下形式(两种写法的作用是完全一致的):
1 | Thread td = new Thread(() -> System.out.println("run")); |
可以说,labmda表达式就是一种简洁的匿名类书写方式,用来增强代码的简洁性和可读性。
Lambda表达式语法
Lambda使用的条件
使用Lambda表达式来实现某个接口时,必须保证该接口只有一个抽象方法。如果该接口有两个或者两个以上的抽象方法,则无法使用Lambda。这里需要注意一点:因为Java8为接口加入了default
和static
方法,这两种方法不是抽象方法,是可以使用lambda的,如下例所示:
- 可以使用Lambda的情况
1 | // 只有一个抽象方法,可以用使用lambda |
1 | InterA interA = () -> System.out.println("canUseLambda"); // 编译通过 |
- 不可以使用Lambda的情况
1 | public interface InterB { |
会报如下的错误:
Lambda 语法
Lambda语法很简单,完整写法就是(参数列表) -> {具体实现代码(有返回类型可以写return)}
。举个例子,假设有一个Rectangle
接口,里面有一个抽象方法,接收两个参数,返回一个double
;同时也有一个Circle
接口,里面有一个方法,接收一个参数,返回一个double
。
1 | public interface Rectangle { |
- 完整定义的方法
1 | Rectangle rect1 = (x, y) -> { |
- 简化写法
1 | // 当具体实现代码只有一行的时候,不需要写大括号,如果有return,则省去return |
- 使用
1 | rect1.cal(4.0,5.0); // 20.0 |
Lambda实战: java.util.function
Lambda主要用在函数式编程里面,而Java8引入了一个新的包用来支持函数式编程,那就是java.util.fuction
。
java.util.function常用接口
接口文档可查看:java.util.function接口文档
接口 | 方法 | 解释 |
---|---|---|
Consumer<T> |
void accept(T t) |
表示”消费者”,接收一个参数(一般用消费t 来实现某个功能) |
Supplier<T> |
T get() |
表示”生产者”,不接收参数,返回一个值 |
Predicate<T> |
bool test(T t) |
预测接口,提供一个参数,返回一个bool 值 |
Function<T, R> |
R apply(T t) |
接收一个参数t ,返回一个值 |
这里写个简单的例子,这些接口一般会经常用在Stream
里,下一章会介绍这些方法的具体应用,这里写个简单的,方便理解。下列方法一个重要的理解方式就是:入参函数使用了另一个入参作为了参数,类方法又调用了入参函数。
- 定义一个类,使用上述接口作为参数
1 | public class DoLambda { |
- 使用这些方法
1 | public static void main(String[] args) { |
Stream API
StreamAPI也是Java8引入的新特性,可以实现集合的串行或者并行的流操作。这是Java8最实用的功能。这里说一个重要的,流只是操作,不改变原集合。
- 创建流
1 | Stream<T> stream1 = 集合<T>对象.stream() // 串行流 |
1 | public class Test04 { |
- 部分流操作
流操作 | 功能 |
---|---|
void forEach(Consumer<? super T> action) |
迭代这个集合中的每一个数据 |
Stream<R> map(Function<? super T, ? extends R> mapper) |
把集合中的每一个T类型数据映射成R类型数据,返回一个R类型的流 |
Stream<T> filter(Predicate<? super T> predicate) |
对集合中每一个数据进行筛选,筛出符合条件的数据(true),并返回一个流 |
Stream<T> limit(long maxSize) |
获取指定数量的流,数量为maxSize(串行有序,并行无序) |
Optional<T> reduce(BinaryOperator<T> accumulator) |
累加器,两两相加,最后返回一个Optional容器(Optional是为了防止空指针,用容器对象.get() 得到T,关于BinaryOperator去看文档) |
T reduce(T identity, BinaryOperator<T> accumulator) |
有初始值的累加,因为有初始值,所以不会出现空指针,直接返回T |
Stream<T> sorted(Comparator<? super T> comparator) |
对集合类进行排序,传入一个比较器,返回一个流对象 |
Object[] toArray() 和 collect(Collectors.toList()) |
对流进行数组或集合类型的转化 |
Stream实战
以上写了一个Person
的列表,要求用流分别实现如下功能:
将人的按照年龄大小排序并打印
筛选出年龄小于50的并打印
打印出总年龄
打印前3个人
把每个人的年龄加100,并打印
将所有的人映射到一个Map集合里面,key是名字,value是年龄
1 | System.out.println("====问题1===="); |
总结
本章内容主要介绍了一下Lambda表达式,同时稍微介绍了一点StreamAPI的知识,这部分知识不难,主要难在架构设计,希望以后我忘记这部分内容的时候能够通过这篇文章回想起来。