java8新特性专题之一、为什么要使用lambda
Lambda表达式?引入Lambda表达式后有哪些好处呢?还好这个朋友对Java8早有准备。不过,如果是看文章的你出去面试,面试官问你这样的问题,你是否也能轻松回答呢?
表达式?
Lambda表达式是一个匿名函数,我们可以这样理解Lambda表达式:Lambda是一段可以传递的代码(能够做到将代码像数据一样进行传递)。使用Lambda表达式能够写出更加简洁、灵活的代码。并且,使用Lambda表达式能够使Java的语言表达能力得到提升。
冗余”的。但是为了书写上面的一行代码,我们不得不在匿名内部类中书写更多的代码。
设计模式汇总——你需要掌握的23种设计模式都在这儿了!》一文来学习设计模式专题。
设计模式汇总——你需要掌握的23种设计模式都在这儿了!》来学习。
设计模式汇总——你需要掌握的23种设计模式都在这儿了!》来学习。
我们先定义一个泛型接口MyPredicate,对传递过来的数据进行过滤,符合规则返回true,不符合规则返回false。
public interface MyPredicate{ /** * 对传递过来的T类型的数据进行过滤 * 符合规则返回true,不符合规则返回false */ boolean filter(T t); }
接下来,我们创建MyPredicate接口的实现类FilterEmployeeByAge来过滤年龄大于或者等于30的员工信息。
public class FilterEmployeeByAge implements MyPredicate{ @Override public boolean filter(Employee employee) { return employee.getAge() >= 30; } }
我们定义一个过滤员工信息的方法,此时传递的参数不仅有员工的信息集合,同时还有一个我们定义的接口实例,在遍历员工集合时将符合过滤条件的员工信息返回。
//优化方式一 public ListfilterEmployee(List list, MyPredicate myPredicate){ List employees = new ArrayList<>(); for(Employee e : list){ if(myPredicate.filter(e)){ employees.add(e); } } return employees; }
接下来,我们写一个测试方法来测试优化后的代码。
@Test public void test4(){ ListemployeeList = this.filterEmployee(this.employees, new FilterEmployeeByAge()); for (Employee e : employeeList){ System.out.println(e); } }
运行test4()方法,输出的结果信息如下所示。
Employee(name=李四, age=38, salary=5555.55)
Employee(name=王五, age=60, salary=6666.66)
写到这里,大家是否有一种豁然开朗的感觉呢?

没错,这就是设计模式的魅力,对于设计模式不熟悉的小伙伴,一定要参照《设计模式汇总——你需要掌握的23种设计模式都在这儿了!》来学习。
我们继续获取当前公司中工资大于或者等于5000的员工信息,此时,我们只需要创建一个FilterEmployeeBySalary类实现MyPredicate接口,如下所示。
public class FilterEmployeeBySalary implements MyPredicate{ @Override public boolean filter(Employee employee) { return employee.getSalary() >= 5000; } }
接下来,就可以直接写测试方法了,在测试方法中继续调用filterEmployee(List方法。
@Test public void test5(){ ListemployeeList = this.filterEmployee(this.employees, new FilterEmployeeBySalary()); for (Employee e : employeeList){ System.out.println(e); } }
运行test5方法,输出的结果信息如下所示。
Employee(name=张三, age=18, salary=9999.99) Employee(name=李四, age=38, salary=5555.55) Employee(name=王五, age=60, salary=6666.66) Employee(name=赵六, age=16, salary=7777.77)
可以看到,使用设计模式对代码进行优化后,无论过滤员工信息的需求如何变化,我们只需要创建MyPredicate接口的实现类来实现具体的过滤逻辑,然后在测试方法中调用filterEmployee(List方法将员工集合和过滤规则传入即可。

这里,问大家一个问题:上面优化代码使用的设计模式是哪种设计模式呢?如果是你,你会想到使用设计模式来优化自己的代码吗?小伙伴们自己先思考一下到底使用的设计模式是什么?文末我会给出答案!
使用设计模式优化代码也有不好的地方:每次定义一个过滤策略的时候,我们都要单独创建一个过滤类!!
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和方法引用的一种展现,详细的使用和原理学习还得往下看。
一、为什么使用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 ListfindGreenApple(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 ListfindApple(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 ListfindApple(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 ListfindApple(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或者说以前这些实现方式的一些弊端:
- 代码繁杂
- 作者特意指出的内部类的弊端
- 其实还有个原因: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 ListfindApple(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表达式具体如何使用
- 2.1这是对上面lambda表达式语法的标注,看了之后应该就已经一目了然了:

对于上面这个,我们可以简化成下面这样:

- 2.1 要使用lambda表达式,那么你自己写的对应接口应该加上 @FunctionalInterface注解,这个注解的使用见下面:

实际上java8源码包
下面包含了许多被@FunctionalInterface注解的接口,他们都可以被lambda表达式服务:

比如上图中打开的
接口中的
方法,他就可以写出以下的lambda表达式:
Functionf = s -> s.length();
它表示给一个String类型的参数,会返回一个Integer类型的返回值。
三、举个栗子
现在让我们加深一下印象:来看看不用lambda和使用lambda构建一个线程的区别:

来源:
https://blog.csdn.net/l1028386804/article/details/105925811
https://blog.csdn.net/aigoV/article/details/102485823