關於java8新特性lambda應用場景之函數式接口的理解


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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM