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