本文主要参照《java8 in action》书中对lambda的讲解,来循序渐进的引入lambda表达式,了解我们为什么,以及怎么样初步学会使用lambda表达式,看完以后你会恍然大悟,不是为了用lambda,而用lambda。
点击获取:《java8 in action》英文原版pdf
在熟悉lambda表达式和方法引用(双冒号操作,后面一篇博文讲)后,我们将能够更加简洁的完成需求编码。
例如:按照苹果颜色完成对一个苹果集的排序:
//1、传统的已经够简化的匿名内部类方式
list.sort(new Comparator() {
@Override
public int compare(Apple o1, Apple o2) {
return o1.getColor().compareTo(o2.getColor());
}
});
//2、使用lambda进一步简化
list.sort((o1,o2)->o1.getColor().compareTo(o2.getColor()));
//2、使用方法引用终极简化
list.sort(Comparator.comparing(Apple::getColor));
上面只是一个很肤浅但直观的一种对lambda和方法引用的一种展现,详细的使用和原理学习还得往下看。
现在我们通过《java8 in action》书中Chapter 2部分的2.1. Coping with changing requirements章节,作者提出的一个问题(如何处理多变的需求)来由浅入深的解释为什么要是用lambda。他给了需求背景:
大概意思就是说,现在你需要做一个农场库存管理的应用程序,农场主的有各种各样的处于变化的需求,你如更好的实现这些可能出现变化的需求,下面详细展开。
1、文中首先提出了一个最基础的需求1::在仓库里的众多苹果中,找出颜色为绿色的苹果
要实现这个需求,我们需要以下代码,:
package com.aigov.java8_newfeatures.lambda;
import lombok.Data;
/**
* @author : aigoV
* @date :2019/10/10
* apple
**/
@Data
public class Apple {
private String color;
private long weight;
public Apple(String color, long weight) {
this.color = color;
this.weight = weight;
}
@Override
public String toString() {
return "Apple{" +
"color='" + color + '\'' +
", weight='" + weight + '\'' +
'}';
}
}
package com.aigov.java8_newfeatures.lambda;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author : aigoV
* @date :2019/10/10
* apple过滤器
* 根据农场主的特定需求找出特定苹果
**/
public class FilterApple {
/** 需求1::在众多苹果中,找出颜色为绿色的苹果**/
public static List findGreenApple(List appleList){
List list = new ArrayList<>();
for (Apple apple : appleList){
if ("green".equals(apple.getColor())){
list.add(apple);
}
}
return list;
}
public static void main(String[] args) {
/** 苹果库存集 **/
List list = Arrays.asList(
new Apple("green",150),
new Apple("yellow",120),
new Apple("green",170)
);
//////////////////////////////////////////////////
List greenApples = findGreenApple(list);
assert greenApples.size() == 2:"过滤后的集合size与预期不符"; //java assert(断言),基本的测试作用,须在idea设置启用。
System.out.print(greenApples);
}
到这,这个简单的需求1就实现了。
2、需求2:需要应对随时变化的需求,比如:不找绿色了,找红色的苹果
这个需求比需求1要求多一点,但其实也很简单,想必大家想的和作者提供的方案一样(当然你也可能想的是再多加一个单独的找红色的方法,,),在过滤器的方法中加入一个color参数,就可以动态满足农场主的需求,代码见下:
package com.aigov.java8_newfeatures.lambda;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author : aigoV
* @date :2019/10/10
* apple过滤器
* 根据农场主的特定需求找出特定苹果
**/
public class FilterApple {
/** 需求2:需要应对随时变化的需求,比如:不找绿色了,找红色的苹果**/
public static List findApple(List appleList,String color){
List list = new ArrayList<>();
for (Apple apple : appleList){
if (color.equals(apple.getColor())){
list.add(apple);
}
}
return list;
}
public static void main(String[] args) {
/** 苹果库存集 **/
List list = Arrays.asList(
new Apple("green",150),
new Apple("yellow",120),
new Apple("green",170)
);
//////////////////////////////////////////////////
List redApples = findApple(list,"red");
System.out.print(redApples);
}
3、需求3:不只是按颜色来找,需要按照重量,颜色去找(也许apple还会有其他属性,需求还会更多变,更复杂,总之你需要满足不停变化的各种需求)
在这里《java8 in action》的作者给我们提供一种解决方案,那就是使用策略模式去应对复杂多变的需求。代码实现:
package com.aigov.java8_newfeatures.lambda;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author : aigoV
* @date :2019/10/10
* apple过滤器
* 根据农场主的特定需求找出特定苹果
**/
public class FilterApple {
/** 需求3:不只是按颜色来找,需要按照重量,颜色去找(你需要满足不停变化的各种需求)**/
//这是抽象出来的过滤接口
public interface AppleFilter{
boolean filter(Apple apple);
}
//这是一个过滤方法
public static List findApple(List appleList,AppleFilter appleFilter){
List list = new ArrayList<>();
for (Apple apple : appleList){
if (appleFilter.filter(apple)){
list.add(apple);
}
}
return list;
}
//按照上面策略模式接口,现在需求是找到颜色为绿色 并且重量>=150的苹果 (这是具体策略的策略类,不同需求建不同策略类)
public static class GreenAnd160weiFilter implements AppleFilter{
@Override
public boolean filter(Apple apple) {
return "green".equals(apple.getColor()) && apple.getWeight()>=160;
}
}
//注意:这里你可能需要为多种多样的需求增添多种多样的策略类,这里就暂写上面那一个
public static void main(String[] args) {
/** 苹果库存集 **/
List list = Arrays.asList(
new Apple("green",150),
new Apple("yellow",120),
new Apple("green",170)
);
//////////////////////////////////////////////////
List redApples2 = findApple(list,new GreenAnd160weiFilter());
//System.out.print(redApples2);
}
4、使用内部类简化策略模式
上面三个由简至繁的需求,实际上使我们在实际开发中的一个缩影,需求总是在变,后台方法也在随之而变,在上面的例子中,我们发现使用策略模式其实已经就可以满足复杂多变的需求了,但是作者觉得这样写代码,还不够简洁,使用策略模式,我们可能需要建很多策略类,这似乎很繁琐。
在此情况下作者又给我们多了一个思路:内部类。上面需求3的策略模式实现代码可以改为以下:
package com.aigov.java8_newfeatures.lambda;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author : aigoV
* @date :2019/10/10
* apple过滤器
* 根据农场主的特定需求找出特定苹果
**/
public class FilterApple {
/** 需求3:不只是按颜色来找,需要按照重量,颜色去找(你需要满足不停变化的各种需求)**/
//这是抽象出来的过滤接口
public interface AppleFilter{
boolean filter(Apple apple);
}
//这是一个过滤方法
public static List findApple(List appleList,AppleFilter appleFilter){
List list = new ArrayList<>();
for (Apple apple : appleList){
if (appleFilter.filter(apple)){
list.add(apple);
}
}
return list;
}
public static void main(String[] args) {
/** 苹果库存集 **/
List list = Arrays.asList(
new Apple("green",150),
new Apple("yellow",120),
new Apple("green",170)
);
//////////////////////////////////////////////////
/** 使用匿名内部类处理多变需求 (实际上就是将需求3的策略类用匿名类直接表示)**/
List yellowApple = findApple(list,new AppleFilter(){
@Override
public boolean filter(Apple apple) {
return "yellow".equals(apple.getColor()) && apple.getWeight()==120;
}
});
//注意:这里你可能需要为多种多样的需求增添多种多样的上面这种匿名类,这里就暂写上面那一个
System.out.print(yellowApple);
}
好了,匿名内部类的使用,我们会发现我们确实不用再去新建很多策略类了,但是不可避免的,我们仍然要为多变的需求去写很多匿名类,并且匿名类的代码也还是不够简洁,而且在书中,作者提到,使用匿名内部类,程序员有时会对内部类里面的成员参数调用产生误解(应该不会吧,,):
好了到这里,我们已经了解到了需求是多变的这个事实,也清楚了在没有lambda之前,我们一般是怎么处理他的,当然从上面例子想必大家也知道了不用lambda或者说以前这些实现方式的一些弊端:
1、现在我们就用lambda表达式来解决上面的需求3的场景,我们先看代码实现,后面解释lambda表达式是如何使用的:
package com.aigov.java8_newfeatures.lambda;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author : aigoV
* @date :2019/10/10
* apple过滤器
* 根据农场主的特定需求找出特定苹果
**/
public class FilterApple {
/** 需求3:不只是按颜色来找,需要按照重量,颜色去找(你需要满足不停变化的各种需求)**/
//这是抽象出来的过滤接口
/** java8中,如果你的接口只有一个方法(default static方法除外),那么可以用这个注解,也就是你这个接口可以用lambda表达式 **/
@FunctionalInterface
public interface AppleFilter{
boolean filter(Apple apple);
}
//这是一个过滤方法
public static List findApple(List appleList,AppleFilter appleFilter){
List list = new ArrayList<>();
for (Apple apple : appleList){
if (appleFilter.filter(apple)){
list.add(apple);
}
}
return list;
}
public static void main(String[] args) {
/** 苹果库存集 **/
List list = Arrays.asList(
new Apple("green",150),
new Apple("yellow",120),
new Apple("green",170)
);
//////////////////////////////////////////////////
List yellowApple2 = findApple(list,(Apple apple)->{
return "yellow".equals(apple.getColor()) && apple.getWeight()==120;
});
System.out.print(yellowApple2);
}
2、解释lambda表达式具体如何使用
对于上面这个,我们可以简化成下面这样:
实际上java8源码包下面包含了许多被@FunctionalInterface注解的接口,他们都可以被lambda表达式服务:
比如上图中打开的 接口中的
方法,他就可以写出以下的lambda表达式:
Function f = s -> s.length();
它表示给一个String类型的参数,会返回一个Integer类型的返回值。
现在让我们加深一下印象:来看看不用lambda和使用lambda构建一个线程的区别: