java8-谓词(predicate)

49次阅读

共计 3523 个字符,预计需要花费 9 分钟才能阅读完成。

传递代码
我们首先看一个例子,假设你有一个 Apple 类,它有一个 getColor 方法,还有一个变量 inventory 保存着一个 Apples 的列表。你可能想要选出所有的绿苹果,并返回一个列表。通常我们用筛选(filter)一词来表达这个概念。在 Java 8 之前,你可能会写这样一个方法 filterGreenApples:
public static List<Apple> filterGreenApples(List<Apple> inventory){
List<Apple> result = new ArrayList<>();
for (Apple apple: inventory){
if (“green”.equals(apple.getColor())) {
result.add(apple);
}
}
return result;
}
但是接下来,有人可能想要选出重的苹果,比如超过 150 克,于是你心情沉重地写了下面这个方法,甚至用了复制粘贴:
public static List<Apple> filterHeavyApples(List<Apple> inventory){
List<Apple> result = new ArrayList<>();
for (Apple apple: inventory){
if (apple.getWeight() > 150) {
result.add(apple);
}
}
return result;
}
我们都知道软件工程中复制粘贴的危险——给一个做了更新和修正,却忘了另一个。嘿,这两个方法只有一行不同:if 里面高亮的那行条件。如果这两个高亮的方法之间的差异仅仅是接受的重量范围不同,那么你只要把接受的重量上下限作为参数传递给 filter 就行了,比如指定 (150, 1000) 来选出重的苹果(超过 150 克),或者指定 (0, 80) 来选出轻的苹果(低于 80 克)。但是,我们前面提过了,Java 8 会把条件代码作为参数传递进去,这样可以避免 filter 方法出现重复的代码。现在你可以写:
public static boolean isGreenApple(Apple apple) {
return “green”.equals(apple.getColor());
}
public static boolean isHeavyApple(Apple apple) {
return apple.getWeight() > 150;
}
static List<Apple> filterApples(List<Apple> inventory, Predicate<Apple> p) {
List<Apple> result = new ArrayList<>();
for (Apple apple: inventory){
if (p.test(apple)) {
result.add(apple);
}
}
return result;
}
要用它的话,你可以写:filterApples(inventory, Apple::isGreenApple); 或者 filterApples(inventory, Apple::isHeavyApple); 什么是谓词?
前面的代码传递了方法 Apple::isGreenApple(它接受参数 Apple 并返回一个 boolean)给 filterApples,后者则希望接受一个 Predicate<Apple> 参数。词 谓词(predicate)在数学上常常用来代表一个类似函数的东西,它接受一个参数值,并返回 true 或 false。你在后面会看到,Java 8 也会允许你写 Function<Apple,Boolean> ——在学校学过函数却没学过谓词的读者对此可能更熟悉,但用 Predicate<Apple> 是更标准的方式,效率也会更高一点儿,这避免了把 boolean 封装在 Boolean 里面。
从传递方法到 Lambda
把方法作为值来传递显然很有用,但要是为类似于 isHeavyApple 和 isGreenApple 这种可能只用一两次的短方法写一堆定义有点儿烦人。不过 Java 8 也解决了这个问题,它引入了一套新记法(匿名函数或 Lambda),让你可以写 filterApples(inventory, (Apple a) -> “green”.equals(a.getColor()) ); 或者 filterApples(inventory, (Apple a) -> a.getWeight() > 150); 甚至 filterApples(inventory, (Apple a) -> a.getWeight() < 80 ||”brown”.equals(a.getColor()) ); 完整的代码为:
public class FilteringApples1 {
public static void main(String[] args) {
List<FilteringApples1.Apple> inventory = Arrays.asList(new FilteringApples1.Apple(80, “green”),
new FilteringApples1.Apple(155, “green”),
new FilteringApples1.Apple(120, “red”));

List<FilteringApples1.Apple> greenApples2 = filterApples(inventory, (FilteringApples1.Apple a) -> “green”.equals(a.getColor()));
System.out.println(greenApples2);

// [Apple{color=’green’, weight=155}]
List<FilteringApples1.Apple> heavyApples2 = filterApples(inventory, (FilteringApples1.Apple a) -> a.getWeight() > 150);
System.out.println(heavyApples2);

// []
List<FilteringApples1.Apple> weirdApples = filterApples(inventory, (FilteringApples1.Apple a) -> a.getWeight() < 80 ||
“brown”.equals(a.getColor()));
System.out.println(weirdApples);
}

public static List<FilteringApples1.Apple> filterApples(List<FilteringApples1.Apple> inventory, Predicate<FilteringApples1.Apple> p) {
List<FilteringApples1.Apple> result = new ArrayList<>();
for (FilteringApples1.Apple apple : inventory) {
if (p.test(apple)) {
result.add(apple);
}
}
return result;
}

public static class Apple {
private int weight = 0;
private String color = “”;

public Apple(int weight, String color) {
this.weight = weight;
this.color = color;
}

public Integer getWeight() {
return weight;
}

public void setWeight(Integer weight) {
this.weight = weight;
}

public String getColor() {
return color;
}

public void setColor(String color) {
this.color = color;
}

public String toString() {
return “Apple{” +
“color='” + color + ‘\” +
“, weight=” + weight +
‘}’;
}
}

}
java8 中内置 filter 函数
static <T> Collection<T> filter(Collection<T> c, Predicate<T> p);
这样你甚至都不需要写 filterApples 了,因为比如先前的调用
filterApples(inventory, (Apple a) -> a.getWeight() > 150);
就可以直接调用库方法 filter:
filter(inventory, (Apple a) -> a.getWeight() > 150);

正文完
 0