lambda 表达式
在 Java 中一个方法的参数只能是一个类或者是基本类型,Java 不支持将函数作为方法的参数,也不支持一个方法的返回值是一个函数。在 JavaScript 中可以有一下的代码:
1 | function addOne(addNum){ |
在 Java 8 中新添加的 lambda 表达式中,允许 Java 进行函数式编程。在类似 JavaScript 中,lambda 表达式的类型是函数,但是在 Java 中,lambda 表达式是对象,必须依附于一类特别的对象类型 函数式接口(Functional Interface)
函数式接口
Java 8 中新增了一个 Functional Interface 注解,对于一个函数式接口有着一下特性:
- 如果一个接口有且仅有一个抽象方法,那么这个接口可以称作是函数式接口。
- 在一个接口上声明 @Functional Interface 注解,那么编译器就会按照函数式接口来要求这个接口。
- 一个接口只有一个抽象方法,并且没有声明 @Functional Interface 注解,编译器会默认这个接口是函数式接口。
对于一个函数式接口有着三种实例化的方式,lambda 表达式,方法引用,构造器引用。
1 | Note that instances of functional interfaces can be created with |
另外对于函数式接口, Java 8 中允许一个接口中有实例方法。如果一个接口中继承了一个 Object 类中的方法,那么这个方法不会计算在仅有的抽象方法中。
1 | If an interface declares an abstract method overriding one of the |
比如,下面的接口虽然有着两个抽象方法,但是其中一个 toString
方法重写自 Object 类,所以这个接口也是符合函数式接口的:
1 |
|
常用的函数式接口:Consumer,Fuction
Consumer 接口
接受一个参数,不返回结果,抽象方法 void accept(T t);
Function 接口
接受一个参数,返回一个结果,抽象方法 R apply(T t);
有两个默认方法,compose,addThen
compose 方法接收一个 Function 的参数,返回一个 Function。返回的 Function 的 apply 方法的参数是接收 Function 执行 apply 方法后的结果。用于在当前 Function 之前执行一个 Function
addThen 方法接受一个 Function 参数,返回一个 Function。返回的 Function 的 apply 方法参数是原本 Function 执行 apply 方法后的结果。用于在当前 Function 之前执行一个 Function
BiFucntion 接口
接受两个参数,返回一个结果,抽象方法 R apply(T t, U u);
有一个默认方法,addThen(Function<? super R, > extends V> after);
返回值是 BiFunction
Predicate 接口
接收一个参数,返回一个 boolean 类型的结果,抽象方法 boolean test(T t);
lambda 表达式入门
一个 lambda 表达式的大致结构
1 | (arguments) -> {body} |
在左边的括号中填写参数,并以 -> 作为分割,右边实现具体的操作。
- 一个 Lambda 表达式可以有零个或多个参数
- 参数的类型既可以明确声明,也可以根据上下文来推断。列如 (int a ) 与 (a) 效果相同。
- 所有的参数需要包含在箭头的左边圆括号内,参数与参数之间用逗号相隔。
- 空圆括号代表参数为空,不可省略
使用 Consumer 接口,传入一个参数,不返回结果
本质上 lambda 是对于匿名内部类的简写,比如对于一个 List 的数据,实现循环通常可以有两中方式,一种是使用普通的 for 循环,另一种是使用增强的 for 循环。Java 8 中对于 List 类型,新增了一个 forEach
方法,方法的参数是需要传入一个 Consumer
接口的实例对象,可以使用匿名内部类来实现:
1 | list.forEach(new Consumer<Integer>() { |
虽然这样可以实现遍历 list,但是似乎相比于一般的 for 循环更为复杂了。而在 Idea 中也会标记这个方法是可以使用 lambda 表达式来简化。点开 Consumer
接口,可以发现,这个接口是一个函数式接口。
1 | list.forEach(integer -> System.out.println(integer)); |
这样通过 lambda 表达式极大的简化了代码,也提高了可读性。
使用 Function 接口,传入一个参数,返回一个结果
Map 接口中定义了 computeIfAbsent()
方法,用于存储,Map 中不存在传入的键时,对应的值。
使用 Function 接口,在 map 中不存在键为 “test” 的时候,在 Map 中存储一个数据。
1 | Map<String,Integer> map = new HashMap<>(); |
使用 lambda 表达式
1 | Map<String,Integer> map = new HashMap<>(); |
在 lambda 表达式出现以前,java 中都是值传递,对于一个方法,预先定义行为,调用的时候传入值。
1 | public int addOne(int v){ |
而 lambda 表达式的出现,允许 Java 进行行为传递。对于一个方法,将行为作为 ’参数‘ 传递进去。
对于上面加一的函数,lambda 表达的方法
1 | public int add(int v, Function<Integer,Integer> function){ |
这样通过一个方法,可以实现多个不同的操作,只需要在调用时传入对应的操作。提升抽象层次,API 重用性更好,更加的灵活。