本文内容针对于oracle的JAVA教程中,lambda表达式章节。
教程围绕“ 如何从 输出所有大于某年龄的职工信息,到 针对性筛选对象并提取信息的通用性函数” 而展开,阅读完整个教程不光能学习到lambda表达式的用法,还可深入体会到所谓 抽象、泛化 的概念。
-
最直接简单的实现如下,代码思路很简单,依次遍历表中所有职工,逐个判断年龄,符合条件则输出。
这里有一个特殊遍历方法,python里也有类似的概念。由于List是Iterable的子类,故可使用For-Each Loop。
public static void printPersonsOlderThan(
List<Person> roster, int age) {
for (Person p : roster) {
if (p.getAge() >= age) {
p.printPerson();
}
}
}
printPersonsOlderThan(roster, 20);
- 那如果想筛选出小于某年龄的呢?重新写一个会显得很麻烦,能否将上一个函数改进一下,让它更general?考虑大于a等价于大于a小于max,小于a等价于大于min小于a,则可将两者抽象合并为某年龄段。
public static void printPersonsWithinAgeRange(
List<Person> roster, int low, int high) {
for (Person p : roster) {
if (low <= p.getAge() && p.getAge() < high) {
p.printPerson();
}
}
}
printPersonsWithinAgeRange(roster, 14, 30);
- 更进一步,如果我们筛选(filter)的判断标准并不局限于年龄呢?比如性别?总不能又重新写一个函数吧?这里将筛选职工这一逻辑进行抽象,从主调函数中剥离,另定义一个接口CheckPerson作为主调函数的形参类型。这样在实际调用主调函数时,只要根据需求实现并实例化接口的一个Local class作为实参传入。
interface CheckPerson {
boolean test(Person p);
}
public static void printPersons(
List<Person> roster, CheckPerson tester) {
for (Person p : roster) {
if (tester.test(p)) {
p.printPerson();
}
}
}
class CheckPersonEligibleForSelectiveService implements CheckPerson {
public boolean test(Person p) {
return p.gender == Person.Sex.MALE &&
p.getAge() >= 18 &&
p.getAge() <= 25;
}
}
printPersons(
roster, new CheckPersonEligibleForSelectiveService());
- 既然已经引入了新的接口,那比Local class更简洁的方式自然是Anonyous class。
printPersons(
roster,
new CheckPerson() {
public boolean test(Person p) {
return p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25;
}
}
);
-
Lambda表达式的引入:CheckPerson由于只含有一个abstract method,故是functional interface(functional interface可能同时含有其他的 非abstract method),因此可以使用lambda表达式。
printPersons(
roster,
(Person p) -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25
);
- JDK中的java.util.function包内预先定义了很多Standard Functional Interfaces ,我们无需自己再定义CheckPerson。
interface Predicate<T> {
boolean test(T t);
}
import java.util.function.Predicate;
public static void printPersonsWithPredicate(
List<Person> roster, Predicate<Person> tester) {
for (Person p : roster) {
if (tester.test(p)) {
p.printPerson();
}
}
}
- 至此,我们完成了“筛选”这一逻辑整个剥离过程,而在筛选出特定对象后,我们直接调用了 printPerson() 方法,获取姓名并打印。实际上,我们也可将其抽象成“析取信息”以及“行为”逻辑。
interface Consumer<T> {
void accept(T t);
}
interface Function<T, R> {
R apply(T t);
}
import java.util.function.Consumer;
import java.util.function.Function;
public static void processPersonsWithFunction(
List<Person> roster,
Predicate<Person> tester,
Function<Person, String> mapper,
Consumer<String> block) {
for (Person p : roster) {
if (tester.test(p)) {
String data = mapper.apply(p);
block.accept(data);
}
}
}
processPersonsWithFunction(
roster,
p -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25,
p -> p.getEmailAddress(),
email -> System.out.println(email)
);
- 我们可以还可以使用 Aggregate 操作来简化上述流程。
roster
.stream()
.filter(
p -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25)
.map(p -> p.getEmailAddress())
.forEach(email -> System.out.println(email));