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类中方法除外)的接口是函数式接口。