Java——Lambda表达式详解
下文中的
AnyType
为任意引用对象简介
Lambda表达式可被理解为简洁地表示可传递的匿名方法的一种方式:它没有名称,但它有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常列表。
在Java中传递代码十分繁琐和冗长(如匿名内部类),Lambda表达式解决了这个问题:它可以让你十分简明地传递代码。
举个例子:利用Lambda表达式,你可以更为简洁地自定义一个Comparator
对象:
原先利用匿名内部类:
1 | Comparator<AnyType> byWeight = new Comparator<AnyType>() { |
利用Lambda表达式之后:
1 | Comparator<AnyType> byWeight = (AnyType a1, AnyType a2) -> a1.getWeight().compareTo(a2.getWeight()); |
标题
结构
- 参数列表——传进的参数;
- 箭头——箭头
->
把参数列表与Lambda主体分隔开,前半部分为参数列表,后半部分为Lambda主体; - Lambda主体——类方法体。
使用
在函数式接口中使用Lambda表达式。
首先了解一下函数式接口的概念:函数式接口就是只定义一个抽象方法的接口,只要接口只定义了一个抽象方法,它就仍然是一个函数式接口。
Lambda表达式允许直接以内联的形式为函数式接口的抽象方法提供实现(保证参数列表及返回值类型与抽象方法保持一致),并把整个表达式作为函数式接口的实例(具体说来,Lambda表达式是函数式接口一个具体实现的实例)。
与匿名内部类相似,但是相对简洁、灵活。
Lambda表达式的使用是基于函数式接口的,所以我们在使用Lambda表达式时需事先定义自己的函数式接口。
为方便使用,JDK1.8在java.util.function
包中提供了几个新的函数式接口,如Predicate
、Consumer
和Function
,均可在相应API文档查询,下面进行具体介绍。
Predicate
接口:java.util.function.Predicate<T>
接口定义了一个名叫test
的抽象方法(boolean test(T t)
),它接受泛型T
对象,并返回一个boolean
;Consumer
接口:java.util.function.Consumer<T>
定义了一个名叫accept
的抽象方法(void accept(T t)
),它接受泛型T
的对象,没有返回值;Function
接口:java.util.function.Function<T, R>
接口定义了一个叫作apply
的方法(R apply(T t)
),它接受一个泛型T
的对象,并返回一个泛型R
的对象。
函数式接口 | 函数定义 |
---|---|
Predicate<T> |
boolean test(T t) |
Consumer<T> |
void accept(T t) |
Function<T,R> |
R apply(T t) |
Supplier<T> |
T get() |
拓展
方法引用:方法引用可以被看作仅仅调用特定方法的Lambda表达式的一种快捷写法。
基本思想:如果一个Lambda表达式代表的只是“直接调用这个方法”,那最好还是用名称来调用它,而不是去描述如何调用它。
如:List
中的sort
方法:
1 | list.sort((AnyType a1, AnyType a2) -> a1.getSize().compareTo(a2.getSize())); |
使用方法引用和java.util.Comparator.comparing
之后:
1 | list.sort(comparing(AnyType::getSize)); |
Lambda及其等效方法引用的例子
| Lambda | 等效的方法引用 |
| :—————-: | :———————-: |
| () -> Thread.currentThread().dumpStack()
| Thread.currentThread()::dumpStack
|
| (str, i) -> str.substring(i)
| String::substring
|
| (String s) -> System.out.println(s)
| System.out::println
|
你可以把方法引用看作针对仅仅涉及单一方法的Lambda表达式的工具,因为你表达同样的事情时要写的代码更少了。
构建方法引用:
- 指向静态方法的方法引用(例如
Integer
的parseInt
方法,写作Integer::parseInt
); - 指向任意类型实例方法的方法引用(例如
String
的length
方法,写作String::length
); - 指向现有对象的实例方法的方法引用(假设你有一个局部变量
expensiveTransaction
用于存放Transaction
类型的对象,它支持实例方法getValue
,那么你就可以写expensiveTransaction::getValue
)。
具体案例,对一个字符串的List排序,忽略大小写,利用String
类中的compareToIgnoreCase
方法
普通Lambda表达式:
1 | List<String> str = Arrays.asList("a","b","A","B"); |
利用方法引用:
1 | List<String> str = Arrays.asList("a","b","A","B"); |
构造函数引用(有无参数应对合适的函数式接口):
1 | //无参构造函数,可对应JDK1.8自带的函数式接口 Supplier的T get() 方法 |