下文中的
AnyType
为任意引用对象
Lambda表达式可被理解为简洁地表示可传递的匿名方法的一种方式:它没有名称,但它有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常列表。
在Java中传递代码十分繁琐和冗长(如匿名内部类),Lambda表达式解决了这个问题:它可以让你十分简明地传递代码。
举个例子:利用Lambda表达式,你可以更为简洁地自定义一个Comparator
对象:
原先利用匿名内部类:
Comparator<AnyType> byWeight = new Comparator<AnyType>() {
public int compare(AnyType a1, AnyType a2){
return a1.getWeight().compareTo(a2.getWeight());
}
};
利用Lambda表达式之后:
Comparator<AnyType> byWeight = (AnyType a1, AnyType a2) -> a1.getWeight().compareTo(a2.getWeight());
->
把参数列表与Lambda主体分隔开,前半部分为参数列表,后半部分为Lambda主体;在函数式接口中使用Lambda表达式。
首先了解一下函数式接口的概念:函数式接口就是只定义一个抽象方法的接口,只要接口只定义了一个抽象方法,它就仍然是一个函数式接口。
Lambda表达式允许直接以内联的形式为函数式接口的抽象方法提供实现(保证参数列表及返回值类型与抽象方法保持一致),并把整个表达式作为函数式接口的实例(具体说来,Lambda表达式是函数式接口一个具体实现的实例)。
与匿名内部类相似,但是相对简洁、灵活。
Lambda表达式的使用是基于函数式接口的,所以我们在使用Lambda表达式时需事先定义自己的函数式接口。
为方便使用,JDK1.8在java.util.function
包中提供了几个新的函数式接口,如Predicate
、Consumer
和Function
,均可在相应API文档查询,下面进行具体介绍。
Predicate
接口:java.util.function.Predicate
接口定义了一个名叫test
的抽象方法(boolean test(T t)
),它接受泛型T
对象,并返回一个boolean
;Consumer
接口:java.util.function.Consumer
定义了一个名叫accept
的抽象方法(void accept(T t)
),它接受泛型T
的对象,没有返回值;Function
接口:java.util.function.Function
接口定义了一个叫作apply
的方法(R apply(T t)
),它接受一个泛型T
的对象,并返回一个泛型R
的对象。函数式接口 | 函数定义 |
---|---|
Predicate |
boolean test(T t) |
Consumer |
void accept(T t) |
Function |
R apply(T t) |
Supplier |
T get() |
方法引用:方法引用可以被看作仅仅调用特定方法的Lambda表达式的一种快捷写法。
基本思想:如果一个Lambda表达式代表的只是“直接调用这个方法”,那最好还是用名称来调用它,而不是去描述如何调用它。
如:List
中的sort
方法:
list.sort((AnyType a1, AnyType a2) -> a1.getSize().compareTo(a2.getSize()));
使用方法引用和java.util.Comparator.comparing
之后:
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表达式:
List<String> str = Arrays.asList("a","b","A","B");
str.sort((s1, s2) -> s1.compareToIgnoreCase(s2));
利用方法引用:
List<String> str = Arrays.asList("a","b","A","B");
str.sort(String::compareToIgnoreCase);
构造函数引用(有无参数应对合适的函数式接口):
//无参构造函数,可对应JDK1.8自带的函数式接口 Supplier的T get() 方法
//普通Lambda表达式
Supplier<AnyType> c1 = () -> new AnyType();
AnyType a1 = c1.get();
//利用方法引用
Supplier<AnyType> c1 = AnyType::new;
AnyType a1 = c1.get();
//有参构造函数,可对应JDK1.8自带的函数式接口Function的R apply(T t) 方法
//普通Lambda表达式
Function<AnyType1, AnyType2> c2 = (t) -> new AnyType2(t);
AnyType2 a2 = c2.apply(t);
//利用方法引用
Function<AnyType1, AnyType2> c2 = AnyType2::new;
AnyType2 a2 = c2.apply(t);