Java企业教程系列
分分钟学会Java8的Lambda
假设有一个电影movie对象,代码如下:
public class Movie {
private String name;
private double rating;
public Movie(String n, double r) {
this.name = n;
this.rating = r;
}
public double getRating() { return rating; }
public String getName() { return name; }
@Override
public String toString() { return "{" + name + ", " + rating + "}"; }
}
有一个集合包含了很多这个Movie对象:
public class Main {
public static void main(String[] args) {
Collection<Movie> movies = new HashSet<>();
movies.add(new Movie("The Shawshank Redemption", 9.2));
movies.add(new Movie("The Dark Knight", 8.9));
movies.add(new Movie("Black Swan", 8.0));
Iterator<Movie> it = movies.iterator();
while (it.hasNext()) {
Movie m = it.next();
if (m.getRating() < 8.5) {
it.remove();
}
}
System.out.println(movies);
}
}
这个是非常简单的普通操作Collection案例,我们一个个展开操作找出电影评分是8.5以下的影片。
有了Java 8的Lambda,如果你需要对一个集合进行筛选,你不必像这样通过迭代器对集合一个个遍历,只要创建一个你需要对集合中每个元素执行动作的接口就可以,这称为predicate 。
interface Predicate<T> {
boolean test(T t);
}
使用这个抽象接口,你可以编写集合的一个通用的方法来对集合中元素应用这个接口进行筛选:
class Collections {
public static <T> void removeAll(Collection<T> coll, Predicate<T> pred) {
Iterator<T> it = coll.iterator();
while (it.hasNext()) {
T t = it.next();
if (pred.test(t)) {
it.remove();
}
}
}
}
这个removeAll就是一个泛型方法。这个泛型方法有两个方法参数类型,其中一个是我们前面定义的接口Predicate.
那么对集合进行筛选的代码可以重新编写如下:
import java.util.*;
public class Main2 {
public static void main(String[] args) {
Collection<Movie> movies = new HashSet<>();
movies.add(new Movie("The Shawshank Redemption", 9.2));
movies.add(new Movie("The Dark Knight", 8.9));
movies.add(new Movie("Black Swan", 8.0));
Collections.removeAll(movies, new Predicate<Movie>() {
@Override
public boolean test(Movie m) {
return m.getRating() < 9.0;
}
}
);
System.out.println(movies);
}
}
这个代码和一开始的Main区别主要是我们将对集合的遍历放入了removeAll方法中,使用了一个内部匿名类new Predicate<Movie>() 实现,在这个Predicate实现子类中,我们实现了接口的方法test(Movie m),在这里对影片进行小于9的判断。
现在我们可以使用Lambda替代这个匿名类,匿名类原来代码如下:
Collections.removeAll(movies, new Predicate<Movie>() {
@Override
public boolean test(Movie m) {
return m.getRating() < 9.0;
}
}
);
使用Lambda替代后如下一行:
Collections.removeAll(movies, m -> m.getRating() < 9.0);
注意到,原来内部类test方法体的代码直接浮现到作为参数传入了。
这样原来集合筛选的代码为:
import java.util.*;
public class Main3 {
public static void main(String[] args) {
Collection<Movie> movies = new HashSet<>();
movies.add(new Movie("The Shawshank Redemption", 9.2));
movies.add(new Movie("The Dark Knight", 8.9));
movies.add(new Movie("Black Swan", 8.0));
Collections.removeAll(movies, m -> m.getRating() < 9.0);
System.out.println(movies);
}
}
那么它内部是如何工作的呢?
从removeAll()的方法签名中, 编译器知道了第二个方法参数的类型是Predicate. 这个Predicate 接口必须只有一个方法. 这个方法只有一个T类型参数,来自于被调用的上下文, 编译器在我们这个案例上下文背景下知道T被绑定到了Movie类型. 使用所有这些信息,它能自己创建一个匿名内部类,正如我们之前实现的匿名类一样,也就是说,Lambda只是一种语法糖,简化了代码。
如果Predicate接口包含更多方法怎么办?编译器会抛出错误说它不是函数接口,一个函数接口只能有一个方法,如果你确实需要很多方法,请使用Java 8的 “Default Methods缺省方法” 特点。