Lambda 表达式的出现旨在解决当接口只有一个抽象函数的时候,采用匿名内部类语法会显得很笨拙,作为 Java 8 里出现的语法糖,实质上对代码的运行没有什么改变(因为编译完成后和我们会恢复成我们正常的代码的样子),我们把一个函数作为另一个函数的参数,接下来就结合一个实际的例子来演示一下 Lambda 表达式的使用技巧。
情景假设
一个班级里面有十个同学,我们来写个方法打印出在某年龄段里面的同学,要求是年龄段在使用的时才给出。
使用匿名内部类来实现
interface JudgeAge {
boolean judgeAge(StudentBean studentBean);
}
定义判断的接口
public static void printStudents(List<StudentBean> studentList, JudgeAge judgeAge) {
for (StudentBean studentBean : studentList) {
if (judgeAge.judgeAge(studentBean)) {
System.out.println(studentBean.toString());
}
}
}
我们先用接口来实现我们要实现的逻辑
printStudents(studentList, new JudgeAge() {
@Override
public boolean judgeAge(StudentBean studentBean) {
return studentBean.getAge() > 10 && studentBean.getAge() < 99;
}
});
接着实现接口,这三段代码是我们使用很多的内部类,当然我们可以现在本地定义一个本地类来实现这个接口,接着把实现的接口作为参数传给方法,但是我们就要设很多无关紧要的类名,不便于代码的简洁,如果用内部类,可以省去为类起名字,当然这还是有点麻烦,接着我们来看用 Lambda 表达式怎么实现上面一样的功能。
使用 Lambda 表达式来代替匿名内部类
如果使用 Lambda 表达式,就只用改变第三段代码,具体的代码如下所示。
printStudents(studentList, s -> s.getAge() > 10 && s.getAge() < 99);
相比于上面用匿名内部类的实现,使用 Lambda 表达式明显少写了很多无用的代码,基本上只用写核心的逻辑就可以了。
使用标准的函数式接口来简化 Lambda 表达式
java 的 java.util.function 包下面有个接口 Predicate<T>
,这个接口只包含 boolean 返回值的 test 方法,可以直接用它来替换上面的接口 JudgeAge。其中接口 Predicate<T>
的形式如下:
interface Predicate<T> {
boolean test(T t);
}
我们这里需要更改我们的方法如下:
private static void printStudents(List<StudentBean> studentList, Predicate<StudentBean> judge) {
for (StudentBean studentBean : studentList) {
if (judge.test(studentBean)) {
System.out.println(studentBean.toString());
}
}
}
printStudents(studentList, s -> s.getAge() > 10 && s.getAge() < 99);
这里的优化在于少写了接口的定义,当然标准的函数式接口不止这些,还有 Iterable<T>
、Function <T, P>
、Consumer<T>
, 加上之前的 Predicate<T>
,这里我们来总结下他们分别用来干什么:
-
Iterable<T>
用来从集合中获取对象 -
Predicate<T>
可以用来做一些判断,从而进行筛选,返回 boolean -
Function <T, P>
映射关系,相当于把输入的东西转换成想要的东西,返回的当然是 P 类 -
Consumer<T>
是用来消费输入的东西,没有返回值
这里方法有的要重写,重写后的代码如下所示:
private static<X, Y> void printStudents(Iterable<X> source, Predicate<X> judge, Function<X, Y> mapper, Consumer<Y> consumer) {
for (X x : source) {
if (judge.test(x)) {
Y data = mapper.apply(x);
consumer.accept(data);
}
}
}
许多实现都放到调用的时候
printStudents(
studentList
, s -> s.getAge() > 10 && s.getAge() < 99
, s -> s.toString()
, str -> System.out.println(str));
当然实现的代码还可以接着简化,向 s -> s.toString() 这种形式的代码直接可以用类名加上::
,加上方法名来表示,简化后的代码如下。
printStudents(
studentList
, s -> s.getAge() > 10 && s.getAge() < 99
, StudentBean::toString
, System.out::println);
使用 Lambda 表达式作为参数进行聚合操作
上面的代码最终简化的显示可以表现为
studentList
.stream()
.filter(s -> s.getAge() > 10 && s.getAge() < 99)
.map(StudentBean::toString)
.forEach(System.out::println);
这是不是很简单呀,聚合操作的说明如下表所示