当前位置 博文首页 > 冰河的专栏:【Java8新特性】你知道Java8为什么要引入Lambda表达

    冰河的专栏:【Java8新特性】你知道Java8为什么要引入Lambda表达

    作者:[db:作者] 时间:2021-07-03 19:13

    写在前面

    这是一道真实的面试题,一个读者朋友出去面试,面试官竟然问他这样一个问题:你说说Java8中为什么引入Lambda表达式?引入Lambda表达式后有哪些好处呢?还好这个朋友对Java8早有准备。不过,如果是看文章的你出去面试,面试官问你这样的问题,你是否也能轻松回答呢?

    什么是Lambda表达式?

    Lambda表达式是一个匿名函数,我们可以这样理解Lambda表达式:Lambda是一段可以传递的代码(能够做到将代码像数据一样进行传递)。使用Lambda表达式能够写出更加简洁、灵活的代码。并且,使用Lambda表达式能够使Java的语言表达能力得到提升。

    匿名内部类

    在介绍如何使用Lambda表达式之前,我们先来看看匿名内部类,例如,我们使用匿名内部类比较两个Integer类型数据的大小。

    Comparator<Integer> com = new Comparator<Integer>() {
        @Override
        public int compare(Integer o1, Integer o2) {
            return Integer.compare(o1, o2);
        }
    };
    

    在上述代码中,我们使用匿名内部类实现了比较两个Integer类型数据的大小。

    接下来,我们就可以将上述匿名内部类的实例作为参数,传递到其他方法中了,如下所示。

     TreeSet<Integer> treeSet = new TreeSet<>(com);
    

    完整的代码如下所示。

    @Test
    public void test1(){
        Comparator<Integer> com = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return Integer.compare(o1, o2);
            }
        };
        TreeSet<Integer> treeSet = new TreeSet<>(com);
    }
    

    我们分析下上述代码,在整个匿名内部类中,实际上真正有用的就是下面一行代码。

     return Integer.compare(o1, o2);
    

    其他的代码本质上都是“冗余”的。但是为了书写上面的一行代码,我们不得不在匿名内部类中书写更多的代码。

    Lambda表达式

    如果使用Lambda表达式完成两个Integer类型数据的比较,我们该如何实现呢?

    Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
    

    看到没,使用Lambda表达式,我们只需要使用一行代码就能够实现两个Integer类型数据的比较。

    我们也可以将Lambda表达式传递到TreeSet的构造方法中,如下所示。

     TreeSet<Integer> treeSet = new TreeSet<>((x, y) -> Integer.compare(x, y));
    

    直观的感受就是使用Lambda表达式一行代码就能搞定匿名内部类多行代码的功能。

    看到这,不少读者会问:我使用匿名内部类的方式实现比较两个整数类型的数据大小并不复杂啊!我为啥还要学习一种新的语法呢?

    在这里插入图片描述

    其实,我想说的是:上面咱们只是简单的列举了一个示例,接下来,咱们写一个稍微复杂一点的例子,来对比下使用匿名内部类与Lambda表达式哪种方式更加简洁。

    对比常规方法和Lambda表达式

    例如,现在有这样一个需求:获取当前公司中员工年龄大于30岁的员工信息。

    首先,我们需要创建一个Employee实体类来存储员工的信息。

    @Data
    @Builder
    @ToString
    @NoArgsConstructor
    @AllArgsConstructor
    public class Employee implements Serializable {
        private static final long serialVersionUID = -9079722457749166858L;
        private String name;
        private Integer age;
        private Double salary;
    }
    

    在Employee中,我们简单存储了员工的姓名、年龄和薪资。

    接下来,我们创建一个存储多个员工的List集合,如下所示。

    protected List<Employee> employees = Arrays.asList(
    		new Employee("张三", 18, 9999.99),
    		new Employee("李四", 38, 5555.55),
    		new Employee("王五", 60, 6666.66),
    		new Employee("赵六", 16, 7777.77),
    		new Employee("田七", 18, 3333.33)
    );
    

    1.常规遍历集合

    我们先使用常规遍历集合的方式来查找年龄大于等于30的员工信息。

    public List<Employee> filterEmployeesByAge(List<Employee> list){
        List<Employee> employees = new ArrayList<>();
        for(Employee e : list){
            if(e.getAge() >= 30){
                employees.add(e);
            }
        }
        return employees;
    }
    

    接下来,我们测试一下上面的方法。

    @Test
    public void test3(){
        List<Employee> employeeList = filterEmployeesByAge(this.employees);
        for (Employee e : employeeList){
            System.out.println(e);
        }
    }
    

    运行test3方法,输出信息如下所示。

    Employee(name=李四, age=38, salary=5555.55)
    Employee(name=王五, age=60, salary=6666.66)
    

    总体来说,查找年龄大于或者等于30的员工信息,使用常规遍历集合的方式稍显复杂了。

    例如,需求发生了变化:获取当前公司中员工工资大于或者等于5000的员工信息。

    此时,我们不得不再次创建一个按照工资过滤的方法。

    public List<Employee> filterEmployeesBySalary(List<Employee> list){
        List<Employee> employees = new ArrayList<>();
        for(Employee e : list){
            if(e.getSalary() >= 5000){
                employees.add(e);
            }
        }
        return employees;
    }
    

    对比filterEmployeesByAge()方法和filterEmployeesBySalary方法后,我们发现,大部分的方法体是相同的,只是for循环中对于条件的判断不同。

    如果此时我们再来一个需求,查找当前公司中年龄小于或者等于20的员工信息,那我们又要创建一个过滤方法了。 看来使用常规方法是真的不方便啊!

    在这里插入图片描述

    这里,问大家一个问题:对于这种常规方法最好的优化方式是啥?相信有不少小伙伴会说:将公用的方法抽取出来。没错,将公用的方法抽取出来是一种优化方式,但它不是最好的方式。最好的方式是啥?那就是使用 设计模式 啊!设计模式可是无数前辈不断实践而总结出的设计原则和设计模式。大家可以查看《设计模式汇总——你需要掌握的23种设计模式都在这儿了!》一文来学习设计模式专题。

    2.使用设计模式优化代码

    如何使用设计模式来优化上面的方法呢,大家继续往下看,对于设计模式不熟悉的同学可以先根据《设计模式汇总——你需要掌握的23种设计模式都在这儿了!》来学习。

    我们先定义一个泛型接口MyPredicate,对传递过来的数据进行过滤,符合规则返回true,不符合规则返回false。

    public interface MyPredicate<T> {
    
        /**
         * 对传递过来的T类型的数据进行过滤
         * 符合规则返回true,不符合规则返回false
         */
        boolean filter(T t);
    }
    

    接下来,我们创建MyPredicate接口的实现类FilterEmployeeByAge来过滤年龄大于或者等于30的员工信息。

    public class FilterEmployeeByAge implements MyPredicate<Employee> {
        @Override
        public boolean filter(Employee employee) {
            return employee.getAge() >= 30;
        }
    }
    

    我们定义一个过滤员工信息的方法,此时传递的参数不仅有员工的信息集合,同时还有一个我们定义的接口实例,在遍历员工集合时将符合过滤条件的员工信息返回。

    //优化方式一
    public List<Employee> filterEmployee(List<Employee> list, MyPredicate<Employee> myPredicate){
        List<Employee> employees = new ArrayList<>();
        for(Employee e : list){
            if(myPredicate.filter(e)){
                employees.add(e);
            }
        }
        return employees;
    }
    

    接下来,我们写一个测试方法来测试优化后的代码。

    @Test
    public void test4(){
        List<Employee> employeeList = 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<Employee>{
        @Override
        public boolean filter(Employee employee) {
            return employee.getSalary() >= 5000;
        }
    }
    

    接下来,就可以直接写测试方法了,在测试方法中继续调用filterEmployee(List<Employee> list, MyPredicate<Employee> myPredicate)方法。

    @Test
    public void test5(){
        List<Employee> employeeList = 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=