Lambda表达式,既然叫做表达式,那么它要表达式的是什么呢?实际上Lambda表达的是一个函数,这个函数只有参数列表和函数体,所以Lambda表达式也被叫做匿名函数。
1 Lambda表达式组成
Lambda由三部分组成,分别是参数列表、箭头、函数体。
参数列表
- 和普通函数一样,Lambda也有参数列表,且表达方式相同,如:(ParamType1 param1, ParamType2 param2, ...)。
- 可以省略参数列表参数类型,如(param1, param2, ...)。
- 如果参数列表只有一个,且省略了参数类型,可以省略(),如param1。
- 参数列表没有参数,()不能省略。
箭头
Lambda表达式的参数列表后紧跟关的是箭头,如->。
函数体
- 和普通函数一样,Lambda也有函数体,如{ System.out.println("hello world!")}。
- 如果函数体只有一条语句,可以省略{},如System.out.println("hello world!")}。
- 如果函数体只有一条语句,且函数有返回值,要省略return关键字。
举例:
- 完整参数列表
- 参数列表省略参数类型
参数列表省略()
参数列表没有参数
函数体只有一条语句,可以省略{}
函数体只有一条语句,且函数有返回值,省略return关键字
通过上面的例子,不难看出Lambda表达式可以被一个对象接收,所以Lambda表达式也能看做是一个对象,相当于匿名类,而接收Lambda表达式的对象类型也被统称为函数式接口。
2 函数式接口
通过对2.1中接收Lambda表达式的对象的类型的研究,我们可以发现,这些接口都有一个共同特征,那就是这些接口中只有一个抽象方法。所以对于函数式接口的定义也就是:只有一个抽象方法的接口就是函数式接口。Lambda表达式只能作用在函数式接口上。
@FunctionalInterface注解,一般的函数式接口,都由@FunctionalInterface注解注释,该注释用于表示该接口就是函数式接口,也能用于校验当前接口是否满足函数式接口的定义。要注意,并不是说只有使用了@FunctionalInterface注解的接口才是函数式接口,只要满足函数式接口的定义的接口都是函数式接口。
3 如何使用Lambda表达式
第1节中,我们用了几个Lambda表达式,并且用一个变量接收了它们,那么,它们该如何工作呢?
以下面这个为例:
我们用Runnable r变量接收了一个Lambda表达式,那么r是一个Runnable接口类型的对象(多态),由于Runnable接口中只有一个抽象方法run(),所以该对象能调用的方法也就是这个run()方法。前面我们也说过,Lambda表达式表达的是一个函数,在这个例子中,该Lambda表达式表达的就是run()这个方法(函数)了。所以我们要使这个Lambda正常工作只需要调用这个run()方法即可。
同理,对于
我们同样只需要调用Comparator接口中的抽象方法即可:
至于上面的Predicate predicate = a -> a > 3;,要如何使用这里不再赘述。
4 类型推断
正如我们上面所说的,Lambda表达式可以省略参数列表中的参数类型,那么如果省略了参数类型,Lambda是如何知道参数是什么类型的呢?Lambda表达式中用到的就是类型推断!
上例中,使用了Comparator接口,下面是Comparator接口的定义,结合上例,Comparator中泛型的类型是T是Integer,所以compare的参数类型的T也就Integer。
这就是在Lambda中使用到的类型推断。
5 引用外部变量
这里我们要明确一个概念外部变量,外部变量指定的是不属性于当前函数或者类的变量,比如Lambda表达式引入一个没有在Lambda表达式中定义的变量,匿名类引用一个没在该类中定义的变量,这些变量都属性外部变量。
使用匿名类,在Java 1.8之前引用的外部变量必须是final类型的,在Java 1.8之后,外部变量可以不用使用final关键字,但是引用的外部变量不允许被改变。
Lambda相当于匿名类,所以对于Lambda表达式要使用外部变量也有同样的限制。
所以在Lambda中有引用到外部变量,该外部变量是不允许被改变的。
6 默认函数式接口
在JDK中也默认提供了一些常用的函数式接口,这些接口在java.util.function包下,这里列举一些基本的函数式接口:
函数式接口函数描述符参数返回类型示例(在集合中的应用)
上述4个函数式接口,是最常用的几个函数式接口。
关于装箱拆箱
如Predicate中的T是泛型,泛型不能接收基本类型,但是使用包装类型会增加内存和计算开销,所以对于基本类型Java 8中也提供了对应的函数式接口,基本类型的函数式接口主要有:
函数式接口函数描述符
更多函数式接口:
关于常用函数式接口总结:
以int类型为例的函数式接口如上,其他类型类同 。要注意,基本类型的函数式接口只针对常用的long、int及double。其他基本类型没有对应的函数式接口。