美文网首页Java基础系列
Java8 新特性 Lambda表达式的基本用法

Java8 新特性 Lambda表达式的基本用法

作者: Chermack | 来源:发表于2020-06-09 12:38 被阅读0次

    一、Lambda表达式

    Lambda表达式是一个匿名函数 ,我们可以把Lambda 表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。有了Lambda表达式使得Java的函数式编程更加方便,代码更加简洁。

    二、Lambda表达式的例子

    一个简单的例子

    分别写两个测试用例实现相同的功能,一个使用匿名内部类,一个使用Lambda表达式,体会二者的差异。
    需求:新建一个list数组,放入一些数,对数组进行排序并打印输出。

    import org.junit.After;
    import org.junit.Before;
    import org.junit.Test;
    import java.util.*;
    import java.util.function.Consumer;
    
    public class TestLambda {
        
        List<Integer> list;
        
        @Before
        public void setup(){
            list = new ArrayList<>();
            list.add(3);
            list.add(5);
            list.add(99);
            list.add(1);
        }
        
        @After
        public void tearDown(){
            list = null;
        }
    
        //匿名内部类
        @Test
        public void test1(){
    
            list.sort( new Comparator<Integer>() {
                @Override
                public int compare(Integer o1, Integer o2) {
                    return Integer.compare(o1, o2);
                }
            });
            list.forEach(new Consumer<Integer>() {
                @Override
                public void accept(Integer integer) {
                    System.out.println(integer);
                }
            });
        }
    
        //Lambda表达式
        @Test
        public void test2(){
            list.sort((o1, o2) -> Integer.compare(o1, o2));
            list.forEach(integer -> System.out.println(integer));
        }
    }
    

    可以看到在使用Lambda表达式能够明显减少代码量,并且具有较好的可读性。

    一个更加复杂的例子

    假设我们有一个Employee类如下:

    class Employee{
        private String name;
        private int age;
        private int salary;
    
        public Employee() {
        }
    
        public Employee(String name, int age, int salary) {
            this.name = name;
            this.age = age;
            this.salary = salary;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public int getSalary() {
            return salary;
        }
    
        public void setSalary(int salary) {
            this.salary = salary;
        }
    
        @Override
        public String toString() {
            return "Employee{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", salary=" + salary +
                    '}';
        }
    }
    

    现在需要对这个类进行一些统计操作:
    1、直接声明函数实现统计功能。(无优化)
    需要对所有员工中年龄大于40的筛选出来,我们可能会写一个筛选函数。

        //一个更加复杂的例子
        @Test
        public void test3() {
            List<Employee> employees = Arrays.asList(
                    new Employee("张三", 20, 12345),
                    new Employee("李四", 50, 23142),
                    new Employee("王二", 42, 2309),
                    new Employee("麻子", 35, 1233)
            );
            //需求:获取年龄大于40的员工;
            List<Employee> result1 = filterEmployees(employees);
            for (Employee e : result1) {
                System.out.println(e);
            }
        }
    
        //获取年龄大于40的员工;
        public List<Employee> filterEmployees(List<Employee> list) {
            List<Employee> employees = new ArrayList<>();
            for (Employee employee : list) {
                if (employee.getAge() >= 40) {
                    employees.add(employee);
                }
            }
            return employees;
        }
    

    如果这样的需求很多,例如要求筛选员工工资大于10000的,我们又需要添加一个筛选函数。

        //需求:获取当前公司中员工工资大于10000的员工信息
        public List<Employee> filterEmployees2(List<Employee> list) {
            List<Employee> employees = new ArrayList<>();
            for (Employee employee : list) {
                if (employee.getSalary() >= 10000) {
                    employees.add(employee);
                }
            }
            return employees;
        }
    

    可见,每增加一个需求,就会新增一个筛选函数。仔细观察会发现,上面两个筛选函数中只有if判断语句中不一样,因此可以将筛选函数提取到接口中,使代码更加清晰。

    2、提取函数公共部分,即使用策略设计模式(针对接口编程而不是实现编程)。
    因为筛选函数中存在大量冗余代码,将这些冗余代码提出,并用接口方式实现。
    定义泛型接口如下,当中只有test一个泛型方法:

    interface MyPredicate<T>{
        boolean test(T t);
    }
    

    当我们需要增加一个筛选功能时,只需要新建一个类实现该接口,即实现test函数来添加筛选条件,例如筛选40岁以上员工的代码如下:

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

    于是,我们可以针对所有的筛选定义一个统一的函数,任何的筛选条件都通过调用该方法进行筛选。

        public List<Employee> fileterEmployee(List<Employee> list, MyPredicate<Employee> myPredicate) {
            List<Employee> employees = new ArrayList<>();
            for (Employee employee : list) {
                if (myPredicate.test(employee)) {
                    employees.add(employee);
                }
            }
            return employees;
        }
    

    具体使用方法如下:

        @Test
        public void test4(){
            List<Employee> employees = Arrays.asList(
                    new Employee("张三", 20, 12345),
                    new Employee("李四", 50, 23142),
                    new Employee("王二", 42, 2309),
                    new Employee("麻子", 35, 1233)
            );
    
            List<Employee> employeeList = fileterEmployee(employees, new FilterEmployeeByAge());
            for (Employee employee : employeeList) {
                System.out.println(employee);
            }
        }
    

    这样一来,所有的筛选都只需要调用filterEmployee这一个方法,只需要传入过滤接口的不同实现类即可。

    3、匿名内部类。可以看到,使用策略设计模式,虽然使得函数的公共部分被提取出来,但针对每一种不同的筛选条件,仍然需要新建一个实现了特定接口的类。如果这个类仅使用一次,没有被复用的机会,显然类的定义就显得多余。于是,可以使用匿名内部类进行替换,我们不需要定义类来实现接口而直接使用new关键字新建一个满足该接口(实现了该接口的方法)的对象。
    例如使用匿名内部类的方法筛选工资小于10000的员工。

        @Test
        public void test5(){
            List<Employee> employees = Arrays.asList(
                    new Employee("张三", 20, 12345),
                    new Employee("李四", 50, 23142),
                    new Employee("王二", 42, 2309),
                    new Employee("麻子", 35, 1233)
            );
    
            List<Employee> employeeList = fileterEmployee(employees, new MyPredicate<Employee>() {
                @Override
                public boolean test(Employee employee) {
                    return employee.getSalary()<10000;
                }
            });
            for (Employee employee : employeeList) {
                System.out.println(employee);
            }
        }
    

    4、Lambda表达式。如果需要实现的接口仅有一个抽象方法,那么我们实现该接口时目的是明确的,就是为了实现该接口中定义的仅有的一个抽象方法的功能。所以匿名内部类也可以省去,直接使用Lambda表达式传递“代码”,告诉这个仅有的抽象方法应该如何实现。上方的匿名内部类也可以替换为如下Lambda表达式。函数式接口的声明可以在接口上方加上@FunctionalInterface注解

        @Test
        public void test6(){
            List<Employee> employees = Arrays.asList(
                    new Employee("张三", 20, 12345),
                    new Employee("李四", 50, 23142),
                    new Employee("王二", 42, 2309),
                    new Employee("麻子", 35, 1233)
            );
    
            List<Employee> employeeList = fileterEmployee(employees, employee -> employee.getSalary()<10000);
            for (Employee employee : employeeList) {
                System.out.println(employee);
            }
        }
    

    5、Stream API。在Lambda表达式进行几乎极简优化的情况下,Java8中还有一项新特性能够更好的对集合进行处理,即Stream API。通过调用Stream API提供的filter方法,甚至不需要定义上述的过滤方法和接口,直接结合Lambda表达式完成数据的筛选操作。

        @Test
        public void test7() {
            List<Employee> employees = Arrays.asList(
                    new Employee("张三", 20, 12345),
                    new Employee("李四", 50, 23142),
                    new Employee("王二", 42, 2309),
                    new Employee("麻子", 35, 1233)
            );
    
    //        List<Employee> employeeList = employees.stream().filter((e)->e.getSalary()<10000).collect(Collectors.toList());
    //        for (Employee employee : employeeList) {
    //            System.out.println(employee);
    //        }
            employees.stream().filter((e)->e.getSalary()<10000).forEach(employee -> System.out.println(employee));
        }
    

    上方注释可以进行筛选并存储到集合,或者直接使用forEach结合lambda表达式直接完成输出,用起来更加方便。

    相关文章

      网友评论

        本文标题:Java8 新特性 Lambda表达式的基本用法

        本文链接:https://www.haomeiwen.com/subject/zfartktx.html