本文主要参照《java8 in action》书中对lambda的讲解,来循序渐进的引入lambda表达式,了解我们为什么,以及怎么样初步学会使用lambda表达式,看完以后你会恍然大悟,不是为了用lambda,而用lambda。
写在前面:
在熟悉lambda表达式和方法引用(双冒号操作,后面一篇博文讲)后,我们将能够更加简洁的完成需求编码。
例如:按照苹果颜色完成对一个苹果集的排序:
//1、传统的已经够简化的匿名内部类方式
list.sort(new Comparator<Apple>() {
@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和方法引用的一种展现,详细的使用和原理学习还得往下看。
一、为什么使用lambda表达式?
现在我们通过《java8 in action》书中Chapter 2部分的2.1. Coping with changing requirements章节,作者提出的一个问题(如何处理多变的需求)来由浅入深的解释为什么要是用lambda。他给了需求背景:
大概意思就是说,现在你需要做一个农场库存管理的应用程序,农场主的有各种各样的处于变化的需求,你如更好的实现这些可能出现变化的需求,下面详细展开。
1、文中首先提出了一个最基础的需求1::在仓库里的众多苹果中,找出颜色为绿色的苹果
要实现这个需求,我们需要以下代码,:
- 1.1 Apple.java 这是一个苹果的bean,我实现了它的构造方法和to String方法(后面的需求举例皆需要用到这个bean):
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 + '\'' +
'}';
}
}
- 1.2同时我们需要创建一个FilterApple.java 过滤器,我把所有实现的相关代码全写在这里面(之后的需求实现也在这个类里,只不过会去掉之前的代码):
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<Apple> findGreenApple(List<Apple> appleList){
List<Apple> list = new ArrayList<>();
for (Apple apple : appleList){
if ("green".equals(apple.getColor())){
list.add(apple);
}
}
return list;
}
public static void main(String[] args) {
/** 苹果库存集 **/
List<Apple> list = Arrays.asList(
new Apple("green",150),
new Apple("yellow",120),
new Apple("green",170)
);
//
List<Apple> 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<Apple> findApple(List<Apple> appleList,String color){
List<Apple> list = new ArrayList<>();
for (Apple apple : appleList){
if (color.equals(apple.getColor())){
list.add(apple);
}
}
return list;
}
public static void main(String[] args) {
/** 苹果库存集 **/
List<Apple> list = Arrays.asList(
new Apple("green",150),
new Apple("yellow",120),
new Apple("green",170)
);
//
List<Apple> 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<Apple> findApple(List<Apple> appleList,AppleFilter appleFilter){
List<Apple> 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<Apple> list = Arrays.asList(
new Apple("green",150),
new Apple("yellow",120),
new Apple("green",170)
);
//
List<Apple> 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<Apple> findApple(List<Apple> appleList,AppleFilter appleFilter){
List<Apple> list = new ArrayList<>();
for (Apple apple : appleList){
if (appleFilter.filter(apple)){
list.add(apple);
}
}
return list;
}
public static void main(String[] args) {
/** 苹果库存集 **/
List<Apple> list = Arrays.asList(
new Apple("green",150),
new Apple("yellow",120),
new Apple("green",170)
);
//
/** 使用匿名内部类处理多变需求 (实际上就是将需求3的策略类用匿名类直接表示)**/
List<Apple> 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或者说以前这些实现方式的一些弊端:
- 代码繁杂
- 作者特意指出的内部类的弊端
- 其实还有个原因:java8 中使用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<Apple> findApple(List<Apple> appleList,AppleFilter appleFilter){
List<Apple> list = new ArrayList<>();
for (Apple apple : appleList){
if (appleFilter.filter(apple)){
list.add(apple);
}
}
return list;
}
public static void main(String[] args) {
/** 苹果库存集 **/
List<Apple> list = Arrays.asList(
new Apple("green",150),
new Apple("yellow",120),
new Apple("green",170)
);
//
List<Apple> yellowApple2 = findApple(list,(Apple apple)->{
return "yellow".equals(apple.getColor()) && apple.getWeight()==120;
});
System.out.print(yellowApple2);
}
2、解释lambda表达式具体如何使用
- 2.1这是对上面lambda表达式语法的标注,看了之后应该就已经一目了然了:
对于上面这个,我们可以简化成下面这样:
- 2.1 要使用lambda表达式,那么你自己写的对应接口应该加上 @FunctionalInterface注解,这个注解的使用见下面:
实际上java8源码包下面包含了许多被@FunctionalInterface注解的接口,他们都可以被lambda表达式服务:
比如上图中打开的 接口中的
方法,他就可以写出以下的lambda表达式:
Function<String,Integer > f = s -> s.length();
它表示给一个String类型的参数,会返回一个Integer类型的返回值。
三、举个栗子
现在让我们加深一下印象:来看看不用lambda和使用lambda构建一个线程的区别:
java8 新特性解析(更新中):
Java8新特性1:lambda表达式入门--由浅入深,从单发步枪迈向自动步枪
Java8新特性3:Stream1——什么是Stream,Stream的特性,如何使用Stream,Stream与Collection集合的区别