lambda是java8的新特性,关于lambda的的应用场景官方解释中有一条是这样的 “任何有函数式接口的地方” ,今天就捋一下这是个什么东西

当我们有一个学生类,

@Data
@AllArgsConstructor
public class Student {

    private String name;
    private Integer age;
    private Integer score;
}

我们现在想通过Student的某个属性来过滤筛选元素,例:1.筛选年龄大于20的;2筛选分数大于79的,我们正常会这么写:

public class Test {
    public static void main(String[] args) {
        List<Student> students = new ArrayList<>();
        students.add(new Student("andy", 24, 80));
        students.add(new Student("rofin", 21, 70));
        students.add(new Student("jesse", 18, 95));
        students.add(new Student("george", 25, 75));
        filterByAge(students);
        System.out.println("---------------");
        filterByScore(students);
    }

    private static void filterByScore(List<Student> students) {
        List<Student> filterStudents = new ArrayList<>();
        for (Student s :
                students) {
            if (s.getScore() > 79) {
                filterStudents.add(s);
            }
        }
        printStudent(filterStudents);
    }

    private static void filterByAge(List<Student> students) {
        List<Student> filterStudents = new ArrayList<>();
        for (Student s :
                students) {
            if (s.getAge() > 20) {
                filterStudents.add(s);
            }
        }
        printStudent(filterStudents);
    }

    private static void printStudent(List<Student> students) {
        students.forEach(System.out::println);
    }

}

 

但是我们发现filterByScore与filterByAge方法体及其相似,或许以后,我们可能还会有根据name属性来做一些筛选的需求,而且我们这里的Student是一个很基础的对象,属性简单,当我们遇到属性较复杂的对象时,难道要每次都要改代码来实现这些过滤方法吗?此时我们有了重构的想法。

我们把过滤的这个动作抽象出来,单独写一个接口,如下:

public interface StudentFilter {
    /**
     * 提供一个过滤学生的接口方法
     * @param student
     * @param condition
     * @return
     */
    boolean filterStudents(Student student, Object condition);
}

然后由具体的实现类负责实现具体的比较方法

public class StudentScoreFilter implements StudentFilter {
    /**
     * 过滤掉小于指定条件的元素
     * @param student
     * @param condition
     * @return
     */
    @Override
    public boolean filterStudents(Student student, Object condition) {
        return student.getScore()>(Integer) condition;
    }
}

public class StudentAgeFilter implements StudentFilter {

    /**
     * 过滤掉小于指定条件的元素
     * @param student
     * @param condition
     * @return
     */
    @Override
    public boolean filterStudents( Student student, Object condition) {
       return student.getAge()>(Integer) condition;
    }

}

有了上面的接口与实现类我们可以再包装一个方法

/**
     * 通过入参来表达调用方是需要哪种过滤器
     * @param students
     * @param studentFilter
     * @param condition
     * @return
     */
    private static List<Student> getByFilter(List<Student> students, StudentFilter studentFilter,Object condition) {
        ArrayList<Student> list = new ArrayList<>();
        for (Student s :
                students) {
            if (studentFilter.filterStudents(s,condition)){
                list.add(s);
            }
        }
        return list;
    }

 

此时,外部调用在调用时,只需要通过传不同的参数就能实现对student集合做不同方式的筛选(姑且认为是策略模式吧…………),看一下外部调用

public static void main(String[] args) {
        List<Student> students = new ArrayList<>();
        students.add(new Student("andy", 24, 80));
        students.add(new Student("rofin", 21, 70));
        students.add(new Student("jesse", 18, 95));
        students.add(new Student("george", 25, 75));

        printStudent(getByFilter(students, new StudentAgeFilter(), 20));
        System.out.println("---------------");
        printStudent(getByFilter(students, new StudentScoreFilter(), 79));

    }

 private static void printStudent(List<Student> students) {
        students.forEach(System.out::println);
    }

到这,我们似乎解决了扩展性的问题。大多时候,的确如此,可惜,我们今天的主角不是它。考虑一下,我们如果现在想通过name的长度大于4来筛选元素,需要加一个StudentNameFilter类实现StudentFilter然后写具体的筛选逻辑,才能达到我们的目的。综合上面的需求一起看,好像定义的这些实现类只干了一件事,就是一个简单的比较,那么是不是可以把这些实现转移到调用的地方来实现?如下使用匿名内部类来实现:

public class Test {
    public static void main(String[] args) {
        List<Student> students = new ArrayList<>();
        students.add(new Student("andy", 24, 80));
        students.add(new Student("rofin", 21, 70));
        students.add(new Student("jesse", 18, 95));
        students.add(new Student("george", 25, 75));

        //内部类的表达方式
        List<Student> students1 = getByFilter(students, new StudentFilter() {
            @Override
            public boolean filterStudents(Student student, Object condition) {
                return student.getAge()>(Integer) condition;
            }
        }, 20);


        List<Student> students2 = getByFilter(students, new StudentFilter() {
            @Override
            public boolean filterStudents(Student student, Object condition) {
                return student.getScore()>(Integer) condition;
            }
        }, 70);
        printStudent(students1);
        System.out.println("------------");
        printStudent(students2);

    }

    /**
     * 通过入参来表达调用方是需要哪种过滤器
     * @param students
     * @param studentFilter
     * @param condition
     * @return
     */
    private static List<Student> getByFilter(List<Student> students, StudentFilter studentFilter, Object condition) {
        ArrayList<Student> list = new ArrayList<>();
        for (Student s :
                students) {
            if (studentFilter.filterStudents(s,condition)){
                list.add(s);
            }
        }
        return list;
    }

    private static void printStudent(List<Student> students) {
        students.forEach(System.out::println);
    }

}

 既然,使用了内部类,内部类的写法,好像可以使用lambda的方式来表达:

public class Test {
    public static void main(String[] args) {
        List<Student> students = new ArrayList<>();
        students.add(new Student("andy", 24, 80));
        students.add(new Student("rofin", 21, 70));
        students.add(new Student("jesse", 18, 95));
        students.add(new Student("george", 25, 75));

        //使用lambda代替内部类
        List<Student> students1 = getByFilter(students, ((student, condition) -> {
            return student.getAge() > (Integer) condition;
        }), 20);
        printStudent(students1);
        System.out.println("------------");
        printStudent(getByFilter(students, (student, condition) -> (student.getScore() > ((Integer) condition)), 70));
        System.out.println("------------");

        //当想使用student的name属性长度进行过滤时不需要再实现一个StudentNameFilter 直接使用lambda表达式即可。
        // 即释义了  任何有函数式接口的地方都可以使用lambda

        printStudent(getByFilter(students, (student, condition) -> {
            return student.getName().length() > (Integer) condition;
        }, 4));
    }


    /**
     * 通过入参来表达调用方是需要哪种过滤器
     *
     * @param students
     * @param studentFilter
     * @param condition
     * @return
     */
    private static List<Student> getByFilter(List<Student> students, StudentFilter studentFilter, Object condition) {
        ArrayList<Student> list = new ArrayList<>();
        for (Student s :
                students) {
            if (studentFilter.filterStudents(s, condition)) {
                list.add(s);
            }
        }
        return list;
    }

    private static void printStudent(List<Student> students) {
        students.forEach(System.out::println);
    }

}

至此 我们的基本可以得出的结论:

任何有函数式接口的地方都可以使用lambda表达式,那什么又是函数式接口呢?就是只有一个抽象方法(Object类中方法除外)的接口是函数式接口。