本文为第一part。让我们一起探索 Lambda 的石榴裙。
借助书中的例子我们可以知道,lambda主要有三部分构成:lambda参数、箭头、lambda主体
接下来将从一个例子出发。
@Data
@AllArgsConstructor
public class Apple {
/**
* 苹果颜色
*/
private String color;
/**
* 苹果重量
*/
private Integer weight;
}
此处使用lombok的 @Data来注解setter和getter方法;使用 @AllArgsConstructor 来定义全参构造函数。
public class LambdaTest {
public static void main(String[] args) {
Apple a1 = new Apple("red", 200);
Apple a2 = new Apple("cyan", 420);
Apple a3 = new Apple("red", 100);
Apple a4 = new Apple("cyan", 250);
Apple a5 = new Apple("red", 300);
// 初始化一堆苹果
List<Apple> apples = List.of(a1, a2, a3, a4, a5);
}
}
此处定义5个不同重量和有红色和青色的苹果。
在没有引入lambda相关的概念时,我们的做法如下。
// 定义过滤条件
private static List<Apple> filterAppleByColor(List<Apple> apples, String color) {
List<Apple> resApples = new ArrayList<>();
for (Apple apple : apples) {
// 根据颜色筛选苹果
if (StringUtils.equals(color, apple.getColor())) {
resApples.add(apple);
}
}
return resApples;
}
// 获取青色苹果
List<Apple> cyanApples = filterAppleByColor(apples, "cyan");
定义 filterAppleByColor 方法来筛选不同颜色的苹果。
// 定义过滤条件
private static List<Apple> filterAppleByWeight(List<Apple> apples, int weight) {
List<Apple> resApples = new ArrayList<>();
for (Apple apple : apples) {
if (apple.getWeight() >= weight) {
resApples.add(apple);
}
}
return resApples;
}
// 筛选大的苹果
List<Apple> bigApples = filterAppleByWeight(apples, 250);
定义 filterAppleByWeight 方法来筛选不同重量的苹果。
// 挑选所有青色且较大的苹果
List<Apple> cyanAndBigApples = filterAppleByWeight(cyanApples, 250);
将所有的青色苹果在按照重量过滤一遍。
通过上面对集合苹果列表的过滤,可以发现一个问题,我们在按照条件过滤苹果的时候,发现除了执行条件的核心地方不一样意外,其它的一模一样。
通过对比我们能否得出这样一个结论:
只定义一个filter,然后根据过滤的条件进行过滤就行了?
伪代码:
public List<Apple> filter(apples, filterCodition);
如果可以那么不管过滤什么条件,我们都可以调用filter,想要的条件,通过 filterCodition 传入即可。
行为参数
通过 public List filter(apples, filterCodition) 得知,我们想要得到自己想要的结果集,那么我们只需要告诉调用方法,我的行为即可。其实这里的 filterCodition 即为行为参数。
filter 方法拿到行为后,该如何做,请继续往下看。
在java8中JDK定义了一个接口 Predicate;该接口源码如下。贴出核心代码。
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
从源码中可以看出,该接口的作用是,接收一个 T 对象,返回一个boolean对象。那么我们上面 过滤条件(apple.getWeight() >= weight 或 StringUtils.equals(color, apple.getColor()))是不是也能这么理解:接收一个Apple对象,返回过滤条件是否为true。
使用 **函数式接口(Predicate 即为函数式接口,后面会讲到) **进行过滤。
private static List<Apple> filter(List<Apple> apples, Predicate<Apple> predicate) {
List<Apple> resApples = new ArrayList<>();
for (Apple apple : apples) {
// 此处根据行为进行过滤
if (predicate.test(apple)) {
resApples.add(apple);
}
}
return resApples;
}
调用filter方法。
// 第一次尝试 - 查询所有红色苹果
List<Apple> redApples1 = filter(apples, new Predicate<Apple>() {
@Override
public boolean test(Apple apple) {
return "red".equals(apple.getColor());
}
});
// 第一次尝试 - 查询所有小苹果
List<Apple> smallApples1 = filter(apples, new Predicate<Apple>() {
@Override
public boolean test(Apple apple) {
return apple.getWeight() < 250;
}
});
为了凸显重要性截图标注代码。
注意:通过filter可以看出我们已经将核心条件抽出到 predicate.test(apple) 这样就可以实现,根据传入的行为过滤。
可以看出通过匿名内部类中的过滤条件替换了 predicate.test(appler) 中的内容,这就是行为参数的核心所在,此处如果不明白,一定要多看几遍熟悉下来,因为这个是关键,很多接口都类似于该道理实现的,比如 Comparator.comparing(Predicate) 也是类似的实现。
通过上面的实现我们发现已经实现了我们想要的效果那就是可以通过传入行为进行过滤内容了。那么这个和我们的主角lambda又有什么关系呢?
上面的实现有个缺点,那就是太啰嗦,如何杜绝啰嗦呢?那就用引入主题lambda。我们可以使用lambda进行如下的改造。
// 第二次尝试 - 查询所有红色苹果
List<Apple> redApples2 = filter(apples, (Apple apple) -> {
return "red".equals(apple.getColor());
});
// 第二次尝试 - 查询所有小苹果
List<Apple> smallApples2 = filter(apples, (Apple apple) -> {
return apple.getWeight() < 250;
});
可以看出使用lambda后,代码简洁了好多。
以上的代码还可以从三个点进行优化。
1.lambda 可以省略参数类型,因为在编译的时候会进行自动推断。
2.当lambda只有一个参数的时候可以省略括号。
3.当lambda return 语句只有一个话的时候可以省略大括号和return。
// 第三次尝试 - 查询所有红色苹果
List<Apple> redApples3 = filter(apples, apple -> "red".equals(apple.getColor()));
// 第三次尝试 - 查询所有小苹果
List<Apple> smallApples3 = filter(apples, apple -> apple.getWeight() < 250);
Apple 类
@Data
@AllArgsConstructor
public class Apple {
/**
* 苹果颜色
*/
private String color;
/**
* 苹果重量
*/
private Integer weight;
}
Main 类
public class LambdaTest {
public static void main(String[] args) {
Apple a1 = new Apple("red", 200);
Apple a2 = new Apple("cyan", 420);
Apple a3 = new Apple("red", 100);
Apple a4 = new Apple("cyan", 250);
Apple a5 = new Apple("red", 300);
// 初始化一堆苹果
List<Apple> apples = List.of(a1, a2, a3, a4, a5);
// 筛选青色苹果
List<Apple> cyanApples = filterAppleByColor(apples, "cyan");
// 筛选大的苹果
List<Apple> bigApples = filterAppleByWeight(apples, 250);
// 挑选所有青色且较大的苹果
List<Apple> cyanAndBigApples = filterAppleByWeight(cyanApples, 250);
// 第一次尝试 - 查询所有红色苹果
List<Apple> redApples1 = filter(apples, new Predicate<Apple>() {
@Override
public boolean test(Apple apple) {
return "red".equals(apple.getColor());
}
});
// 第一次尝试 - 查询所有小苹果
List<Apple> smallApples1 = filter(apples, new Predicate<Apple>() {
@Override
public boolean test(Apple apple) {
return apple.getWeight() < 250;
}
});
// 第二次尝试 - 查询所有红色苹果
List<Apple> redApples2 = filter(apples, (Apple apple) -> {
return "red".equals(apple.getColor());
});
// 第二次尝试 - 查询所有小苹果
List<Apple> smallApples2 = filter(apples, (Apple apple) -> {
return apple.getWeight() < 250;
});
// 第三次尝试 - 查询所有红色苹果
List<Apple> redApples3 = filter(apples, apple -> "red".equals(apple.getColor()));
// 第三次尝试 - 查询所有小苹果
List<Apple> smallApples3 = filter(apples, apple -> apple.getWeight() < 250);
}
private static List<Apple> filterAppleByColor(List<Apple> apples, String color) {
List<Apple> resApples = new ArrayList<>();
for (Apple apple : apples) {
if (StringUtils.equals(color, apple.getColor())) {
resApples.add(apple);
}
}
return resApples;
}
private static List<Apple> filterAppleByWeight(List<Apple> apples, int weight) {
List<Apple> resApples = new ArrayList<>();
for (Apple apple : apples) {
if (apple.getWeight() >= weight) {
resApples.add(apple);
}
}
return resApples;
}
private static List<Apple> filter(List<Apple> apples, Predicate<Apple> predicate) {
List<Apple> resApples = new ArrayList<>();
for (Apple apple : apples) {
// 此处根据行为进行过滤
if (predicate.test(apple)) {
resApples.add(apple);
}
}
return resApples;
}
}