Lambda表达式
Java8中引入了Lambda表达式,Lambda表达式其实就是匿名函数。匿名函数就是没有方法名的函数。可以看下面一个例子,假如定义一个打印Hello,world的线程,之前可能这样写
通过匿名内部类的方式,将打印Hello,world告诉Thread,在Java8中,引入Lambda表达式大大简化了代码,可以直接将行为进行传递
Lambda表达式的定义
|
|
其中(int x, int y)是Lambda表达式的参数,->箭头后面的x+y是函数的主体部分。返回值为x+y。
在上面的线程打印例子中,
这段代码也是Lambda表达式,输入参数为空,返回类型也是void。
函数式接口
函数式接口(Functional Interface)就是指在那些只有一个抽象方法的接口(不考虑default方法)。可以使用@FunctionalInterface来标注一个接口使它成为函数式接口。比如Runnable接口,就是一个函数式接口,它恰好只有一个抽象方法run()。
Lambda表达式允许你直接以内联的形式为函数式接口的抽象方法提供实现,并把整个表达式作为函数式接口的实例。
现在定义一个函数式接口
定义一个静态方法
使用匿名内部类实现加法
使用Lambda表达式
相比匿名内部类简洁许多了,还可以更简单呢。
直接将函数式接口的具体实现作为参数传递
Java8 API中的函数式接口
接口 | 参数 | 返回类型 | 函数描述符 | 抽象方法 | 场景 |
---|---|---|---|---|---|
Predicate |
T | boolean | T -> boolean | boolean test(T t); | 判断一条语句表达式的布尔值 |
Consumer |
T | void | T -> void | void accept(T t); | 执行接受参数的行为,不返回任何值 |
Function |
T | R | T -> R | R apply(T t); | 执行接受参数的行为并返回其他类型的结果R |
Supplier |
None | T | () -> T | T get(); | 提供获取一个T类型 |
UnaryOperator |
T | T | T -> T | 继承自Function |
接受一个T类型的参数并返回相同类型的参数 |
BinaryOperator |
T, T | T | (T, T) -> T | 继承自BiFunction |
接受两个T类型的参数,并返回一个T类型的参数 |
BiPredicate |
L, R | boolean | (L, R) -> boolean | boolean test(T t, U u); | 接受两个参数,返回一个布尔值 |
BiConsumer |
T, U | void | (T, U) -> void | void accept(T t, U u); | 执行接受两个参数的行为,不返回任何值 |
BiFunction |
T, U | R | (T, U) -> R | R apply(T t, U u); | 执行接受两个参数的行为,并返回R类型的结果 |
Java8 API所提供的函数式接口在java.util.function包中,由于提供的函数式接口均是泛型接口,比如Predicate
举例来说,IntPredicate接口在接受int类型的参数输入时就不会去自动装箱,而使用Predicate接口接受int类型参数输入会产生额外的性能开销。类似的有ToDoubleFunction接口,这种是输出double基本类型时避免自动装箱,还有IntToDoubleFunction接口,在输入int类型输出double类型时避免了自动装箱。
类型检查与类型推断
在Java7中引入了菱形操作符,使得编译器可以自动推断出类型参数。
Lambda表达式可以根据上下文推断出类型,比如
(int x, int y) -> x + y; 可以写成(x, y) -> x + y;
函数接口也会根据泛型参数类型进行判断。
方法引用
方法引用可以重复地使用现有的方法定义,而不用每次使用时重新去定义。
这段代码等同于
方法引用在以下三种情况时可以使用
指向静态方法的方法引用
比如String的静态方法valueOf()指向任意类型实例方法的方法引用
比如String的实例length()指向现有对象的实例方法的方法引用
比如已有对象student的实例方法getName()