Lambda表达式初步与函数式接口
“Lambda 表达式”(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。Lambda表达式可以表示闭包(注意和数学传统意义上的不同)。
为何需要Lambda表达式
- 在Java中,我们无法将函数作为参数传递给一个方法,也无法声明返回一个函数的方法。
- 在JavaScript中,函数参数一个函数,返回值是另一个函数的情况是非常常见的;JavaScript是一门非常典型的函数式语言。
Java匿名内部类示例:
1 | new Thread(new Runnable() { |
这样写是有点繁琐的,在Java8中可以直接下面这样写
1 | new Thread(() -> { |
在Java8的循环中,我们也可以很方便的使用Lambda表达式。
示例如下:
1 | List<Integer> list = Arrays.asList(1,2,3,4,5); |
看forEach的方法源码
1 | default void forEach(Consumer<? super T> action) { |
接受了一个Consumer参数,这个接口是JDK8新增的一个函数式接口。
什么是函数式接口?
- 一个接口,有且只有一个抽象方法,这个接口就称为函数式接口。
- 如果我们在某个接口上声明了@FunctionalInterface注解,那么编译器就会按照函数式接口的定义来要求该接口。
- 如果某个接口只有抽象方法,但我们并没有给该接口声明@FunctionalInterface注解,那么编译器依旧会将该接口看做是函数式接口。
1 |
|
我们来试试自己写一个函数式接口。
1 |
|
Lambda表达式的作用
Lambda表达式为Java添加了缺失的函数式编程特性,使我们能将函数当作一等公民看待。
在将函数作为一等公民的语言中,Lambda表达式的类型是函数。但在Java中,Lambda表达式是对象,他们必须依附于一类特别的对象类型—-函数式接口(functional interface)。
例子
下面是用lambda表达式和stream来对一个列表的字符串进行大写字母转换。
1 | List<String> list = Lists.newArrayList("hello", "world", "hello world"); |
上面看到有2个冒号的地方,这个叫做方法引用,方法引用有四种方式,这是其中一种,通过类的方式引用。
1 | Function<String, String> function = String::toUpperCase; |
那么对象会被当做lambda表达式的第一个参数传入,上面的代码就相当于”hello”.toUpperCase();
下面演示一个Comparator的例子
1 | List<String> names = Arrays.asList("zhangsan", "lisi", "wangwu", "zhaoliu"); |
这就是一个倒序排序,Collections.sort()的第二个参数就是一个Comparator对象,我们用lambda表示来写的,看一下Comparator是声明为函数式接口。所以可以用lambda来写。
1 |
|
Java Lambda基本语法
- Java中的Lambda表达式基本语法
- (arg) -> (body)
- 比如
- (arg1, arg2) -> {bodu}
- (type1 arg1, type2 arg2…) -> {body}
示例
- (int a, int b) -> { return a + b; }
- () -> System.out.println(“Hello World”);
- (String s) -> {System.out.println(s);}
- () -> 42
Java Lambda结构
- 一个Lambda表达式可以有零个或多个参数
- 参数的类型既可以明确声明,也可以根据上下文来推断。例如:(int a)与(a)效果相同。
- 所有参数需包含在圆括号内,参数之间用逗号相隔。例如:(a,b)或(int a,int b)或(String a,int b, float c)
- 空圆括号代表参数集为空。例如:() -> 42
- 当只有一个参数,且其类型可推导时,圆括号()可省略。例如:a -> return a * a;
- Lambda表达式的主体可包含零条或多条语句。
- 如果Lambda表达式的主体只有一条语句,花括号{}可省略。匿名函数的返回类型与该主体表达式一致。
- 如果Lambda表达式的主体包含一条以上语句,则表达式必须包含在花括号{}中(形成代码块)。匿名函数的返回类型与代码块的返回类型一致,若没有返回则为空。
主要接口详解
Function接口
1 | public class FunctionTest { |
compose和andThen
compose()方法,它接受一个Function,也返回一个Function,结果就是执行参数里的apply,再执行本对象的apply。
andThen()方法则相反,是先执行本对象的apply,再执行参数Function的apply。
1 | public class FunctionTest2 { |
BiFunction
接受2个参数,返回一个值的函数式接口。
1 | public class PersonTest { |
Predicate
判断用的函数式接口
1 | public class PredicateTest { |
代码测试
1 | public class PredicateTest2 { |
Supplier
简单测试
1 | public class SupplierTest { |
java.util.function包下面还有很多函数式接口,无非就是0参数,1个参数,2个参数的接口,用法都是一样的。
1 |
|