Java8 新特性 Lambda表達(dá)式的基本用法

一、Lambda表達(dá)式

Lambda表達(dá)式是一個(gè)匿名函數(shù) ,我們可以把Lambda 表達(dá)式理解為是一段可以傳遞的代碼(將代碼像數(shù)據(jù)一樣進(jìn)行傳遞)。有了Lambda表達(dá)式使得Java的函數(shù)式編程更加方便,代碼更加簡(jiǎn)潔。

二、Lambda表達(dá)式的例子

一個(gè)簡(jiǎn)單的例子

分別寫(xiě)兩個(gè)測(cè)試用例實(shí)現(xiàn)相同的功能,一個(gè)使用匿名內(nèi)部類,一個(gè)使用Lambda表達(dá)式,體會(huì)二者的差異。
需求:新建一個(gè)list數(shù)組,放入一些數(shù),對(duì)數(shù)組進(jìn)行排序并打印輸出。

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;
    }

    //匿名內(nèi)部類
    @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表達(dá)式
    @Test
    public void test2(){
        list.sort((o1, o2) -> Integer.compare(o1, o2));
        list.forEach(integer -> System.out.println(integer));
    }
}

可以看到在使用Lambda表達(dá)式能夠明顯減少代碼量,并且具有較好的可讀性。

一個(gè)更加復(fù)雜的例子

假設(shè)我們有一個(gè)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 +
                '}';
    }
}

現(xiàn)在需要對(duì)這個(gè)類進(jìn)行一些統(tǒng)計(jì)操作:
1、直接聲明函數(shù)實(shí)現(xiàn)統(tǒng)計(jì)功能。(無(wú)優(yōu)化)
需要對(duì)所有員工中年齡大于40的篩選出來(lái),我們可能會(huì)寫(xiě)一個(gè)篩選函數(shù)。

    //一個(gè)更加復(fù)雜的例子
    @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的,我們又需要添加一個(gè)篩選函數(shù)。

    //需求:獲取當(dāng)前公司中員工工資大于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;
    }

可見(jiàn),每增加一個(gè)需求,就會(huì)新增一個(gè)篩選函數(shù)。仔細(xì)觀察會(huì)發(fā)現(xiàn),上面兩個(gè)篩選函數(shù)中只有if判斷語(yǔ)句中不一樣,因此可以將篩選函數(shù)提取到接口中,使代碼更加清晰。

2、提取函數(shù)公共部分,即使用策略設(shè)計(jì)模式(針對(duì)接口編程而不是實(shí)現(xiàn)編程)。
因?yàn)楹Y選函數(shù)中存在大量冗余代碼,將這些冗余代碼提出,并用接口方式實(shí)現(xiàn)。
定義泛型接口如下,當(dāng)中只有test一個(gè)泛型方法:

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

當(dāng)我們需要增加一個(gè)篩選功能時(shí),只需要新建一個(gè)類實(shí)現(xiàn)該接口,即實(shí)現(xiàn)test函數(shù)來(lái)添加篩選條件,例如篩選40歲以上員工的代碼如下:

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

于是,我們可以針對(duì)所有的篩選定義一個(gè)統(tǒng)一的函數(shù),任何的篩選條件都通過(guò)調(diào)用該方法進(jìn)行篩選。

    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);
        }
    }

這樣一來(lái),所有的篩選都只需要調(diào)用filterEmployee這一個(gè)方法,只需要傳入過(guò)濾接口的不同實(shí)現(xiàn)類即可。

3、匿名內(nèi)部類??梢钥吹?,使用策略設(shè)計(jì)模式,雖然使得函數(shù)的公共部分被提取出來(lái),但針對(duì)每一種不同的篩選條件,仍然需要新建一個(gè)實(shí)現(xiàn)了特定接口的類。如果這個(gè)類僅使用一次,沒(méi)有被復(fù)用的機(jī)會(huì),顯然類的定義就顯得多余。于是,可以使用匿名內(nèi)部類進(jìn)行替換,我們不需要定義類來(lái)實(shí)現(xiàn)接口而直接使用new關(guān)鍵字新建一個(gè)滿足該接口(實(shí)現(xiàn)了該接口的方法)的對(duì)象。
例如使用匿名內(nèi)部類的方法篩選工資小于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表達(dá)式。如果需要實(shí)現(xiàn)的接口僅有一個(gè)抽象方法,那么我們實(shí)現(xiàn)該接口時(shí)目的是明確的,就是為了實(shí)現(xiàn)該接口中定義的僅有的一個(gè)抽象方法的功能。所以匿名內(nèi)部類也可以省去,直接使用Lambda表達(dá)式傳遞“代碼”,告訴這個(gè)僅有的抽象方法應(yīng)該如何實(shí)現(xiàn)。上方的匿名內(nèi)部類也可以替換為如下Lambda表達(dá)式。函數(shù)式接口的聲明可以在接口上方加上@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表達(dá)式進(jìn)行幾乎極簡(jiǎn)優(yōu)化的情況下,Java8中還有一項(xiàng)新特性能夠更好的對(duì)集合進(jìn)行處理,即Stream API。通過(guò)調(diào)用Stream API提供的filter方法,甚至不需要定義上述的過(guò)濾方法和接口,直接結(jié)合Lambda表達(dá)式完成數(shù)據(jù)的篩選操作。

    @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));
    }

上方注釋可以進(jìn)行篩選并存儲(chǔ)到集合,或者直接使用forEach結(jié)合lambda表達(dá)式直接完成輸出,用起來(lái)更加方便。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容