Lambda表達(dá)式

一、為什么使用Lambda表達(dá)式?

Lambda是一個(gè)匿名函數(shù),我們可以把Lambda表達(dá)式理解為是一段可以傳遞的代碼(將代碼像數(shù)據(jù)一樣進(jìn)行傳遞)。可以寫出更簡(jiǎn)潔、更靈活的代碼。作為一種更緊湊的代碼風(fēng)格,使Java的語言表達(dá)能力得到了提升。

二、Lambda表達(dá)式初識(shí)

下面我們來看一下幾個(gè)Lambda表達(dá)式的例子:

  • 從匿名類到Lambda的轉(zhuǎn)換

    Runnable r = new Runnable() {
              @Override
              public void run() {
                  System.out.println("Hello World!" );
              }
          };
    
    • Lambda表達(dá)式

      Runnable r1 = () -> System.out.println("Hello Lambda!");
      
      • 原來使用匿名內(nèi)部類作為參數(shù)傳遞

        
              TreeSet<String> ts2 = new TreeSet<>(new Comparator<String>(){
                  @Override
                  public int compare(String o1, String o2) {
                      return Integer.compare(o1.length(), o2.length());
                  }
                  
              });
        
      • Lambda表達(dá)式作為參數(shù)傳遞

TreeSet<String> ts2=new TreeSet<>(
(o1,o2) -> Integer.compare(o1.lenrth(),o2.length())
);

三、Lambda表達(dá)式引入點(diǎn)

要求:現(xiàn)在有一個(gè)員工集合,要求按照員工的薪資、員工的年齡得到符合要求的員工集合。

首先,我們定義一個(gè)員工類,Employee.java

public class Employee {

    private int id;
    private String name;
    private int age;
    private double salary;

    public Employee() {
    }

    public Employee(String name) {
        this.name = name;
    }

    public Employee(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Employee(int id, String name, int age, double salary) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.salary = salary;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    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 double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    @Override
    public String toString() {
        return "Employee [id=" + id + ", name=" + name + ", age=" + age + ", salary=" + salary + "]";
    }

}

第一種解決方案:也是我們比較常規(guī)思維,就是定義一個(gè)方法,對(duì)應(yīng)要求得到指定的員工集合,這里我們就只看“獲取公司中工資大于 5000 的員工信息”定義的方法如下:

public List<Employee> filterEmployeeSalary(List<Employee> emps){
        List<Employee> list = new ArrayList<>();
        
        for (Employee emp : emps) {
            if(emp.getSalary() >= 5000){
                list.add(emp);
            }
        }
        
        return list;
    }
    

然后每一種要求對(duì)應(yīng)一種方法,但是這種會(huì)很冗余,本來就一個(gè)條件不同,但是要寫這么多相同的代碼。這樣,我們就要來優(yōu)化了,我們更好的思維就是通過設(shè)計(jì)模式來操作了。

第二種解決方案:利用設(shè)計(jì)模式中的策略模式來優(yōu)化。

首先,我們定義一個(gè)接口

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

然后只要有一個(gè)要求,我就定義一個(gè)對(duì)應(yīng)的類實(shí)現(xiàn)這個(gè)接口,這里依然是使用上述的要求:獲取公司中工資大于 5000 的員工信息。

public class FilterEmployeeForSalary implements MyPredicate<Employee> {

    @Override
    public boolean test(Employee t) {
        return t.getSalary() >= 5000;
    }

}

然后定義一個(gè)方法。如下

public List<Employee> filterEmployee(List<Employee> emps, MyPredicate<Employee> mp){
        List<Employee> list = new ArrayList<>();
        
        for (Employee employee : emps) {
            if(mp.test(employee)){
                list.add(employee);
            }
        }
        
        return list;
    }

最后在測(cè)試代碼里面匿名內(nèi)部類。

@Test
    public void test5(){
        List<Employee> list = filterEmployee(emps, new MyPredicate<Employee>() {
            @Override
            public boolean test(Employee t) {
                return t.getId() <= 103;
            }
        });
        
        for (Employee employee : list) {
            System.out.println(employee);
        }
    }

這樣就能得到要求的員工集合了。

但是這樣看來,還是有點(diǎn)冗余,因?yàn)槊恳粋€(gè)要求我都要?jiǎng)?chuàng)建一個(gè)類。所以,Lambda表達(dá)式就引入了。

第三種方案:引進(jìn)Lambda表達(dá)式

@Test
    public void test6(){
        List<Employee> list = filterEmployee(emps, (e) -> e.getAge() <= 35);
        list.forEach(System.out::println);
        
        System.out.println("------------------------------------------");
        
        List<Employee> list2 = filterEmployee(emps, (e) -> e.getSalary() >= 5000);
        list2.forEach(System.out::println);
    }

四、Lambda表達(dá)式語法

Lambda表達(dá)式在Java語言中引入了一個(gè)新的語法元素和操作符。這個(gè)操作符為“->”,該操作符被稱為Lambda操作符或箭頭操作符。它將Lambda分為兩個(gè)部分:

左側(cè):指定了Lambda表達(dá)式需要的所有參數(shù)。

右側(cè):指定了Lambda體,即Lambda表達(dá)式要執(zhí)行的功能。

1、語法格式一:無參數(shù),無返回值
() -> System.out.println("Hello Lambda!");

示例代碼:

    @Test
    public void test1(){
        int num = 0;//jdk 1.7 前,必須是 final
        
        Runnable r = new Runnable() {
            @Override
            public void run() {
                System.out.println("Hello World!" + num);
            }
        };
        
        r.run();
        
        System.out.println("-------------------------------");
        //無參無返回值
        Runnable r1 = () -> System.out.println("Hello Lambda!");
        r1.run();
    }
2、語法格式二:有一個(gè)參數(shù),并且無返回值
(x) -> System.out.println(x)

示例代碼:

@Test
    public void test2(){
        Consumer<String> con = (x) -> System.out.println(x);
        con.accept("你是大傻子!");
    }
3、語法格式三:若只有一個(gè)參數(shù),小括號(hào)可以省略不寫
x -> System.out.println(x)

示例代碼:

    @Test
    public void test2(){
        Consumer<String> con = x -> System.out.println(x);
        con.accept("你是大傻子!");
    }
4、語法格式四:有兩個(gè)以上的參數(shù),有返回值,并且 Lambda 體中有多條語句
Comparator<Integer> com = (x, y) -> {
            System.out.println("函數(shù)式接口");
            return Integer.compare(x, y);
        };

示例代碼:

@Test
    public void test3(){
        Comparator<Integer> com = (x, y) -> {
            System.out.println("函數(shù)式接口");
            return Integer.compare(x, y);
        };
    }
5、語法格式五:若 Lambda 體中只有一條語句, return 和 大括號(hào)都可以省略不寫
Comparator<Integer> com = (x, y) -> Integer.compare(x, y);

示例代碼:

@Test
    public void test4(){
        Comparator<Integer> com = (x, y) -> Integer.compare(x, y);
    }
6、 語法格式六:Lambda 表達(dá)式的參數(shù)列表的數(shù)據(jù)類型可以省略不寫,因?yàn)镴VM編譯器通過上下文推斷出,數(shù)據(jù)類型,即“類型推斷”
(Integer x, Integer y) -> Integer.compare(x, y);

示例代碼:

    @Test
    public void test5(){
//      String[] strs;
//      strs = {"aaa", "bbb", "ccc"};
//字符串?dāng)?shù)組定義時(shí)不能分兩步寫,這樣寫是因?yàn)轭愋屯茢?。所以編譯才能通過的。
        List<String> list = new ArrayList<>();
// ArrayList<>的類型可以不用寫,這樣也是因?yàn)轭愋屯茢嗟摹?        show(new HashMap<>());
    }

    public void show(Map<String, Integer> map){
        
    }
語法小總結(jié):

上聯(lián):左右遇一括號(hào)省

下聯(lián):左側(cè)推斷類型省

橫批:能省則省

五、類型推斷(比較重要)

上述的語法六,Lambda表達(dá)式中的參數(shù)類型都是由編譯器推斷得出的。Lambda表達(dá)式中無需指定類型,程序依然可以編譯,這是因?yàn)閖avac根據(jù)程序的上下文,在后臺(tái)推斷出了參數(shù)的類型。Lambda表達(dá)式的類型依賴于上下文環(huán)境,是由編譯器推斷出來的。這就是所謂的“類型推斷”。

六、函數(shù)式接口

這里就稍微講一下,下面的文章會(huì)詳細(xì)講解

1、什么是函數(shù)式接口?

(1)、只包含一個(gè)抽象方法的接口,稱為函數(shù)式接口。

(2)、你可以通過Lambda表達(dá)式來創(chuàng)建該接口的對(duì)象。(若Lambda表達(dá)式拋出一個(gè)受檢異常,那么該異常需要在目標(biāo)接口的抽象方法上進(jìn)行聲明)。

(3)、我們可以在任意函數(shù)式接口上使用@FunctionalInterface注解,這樣做可以檢查它是否是一個(gè)函數(shù)式接口,同時(shí)javadoc也會(huì)包含一條聲明,說明這個(gè)接口是一個(gè)函數(shù)式接口。

2、自定義函數(shù)式接口

示例代碼:

@FunctionalInterface
public interface MyFun {
    public Integer getValue(Integer num);
}

七、Lambda練習(xí)

1、調(diào)用Collecions.sort()方法,通過定制排序比較兩個(gè)Employee(先按年齡比,年齡相同按姓名比),使用Lambda作為參數(shù)傳遞。

步驟一:先創(chuàng)建一個(gè)pojo

package com.nieshenkuan.pojo;

public class Employee {

    private int id;
    private String name;
    private int age;
    private double salary;

    public Employee() {
    }

    public Employee(String name) {
        this.name = name;
    }

    public Employee(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Employee(int id, String name, int age, double salary) {
        this.id = id;
        this.name = name;
        this.age = age;
        this.salary = salary;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    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 double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public String show() {
        return "測(cè)試方法引用!";
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + age;
        result = prime * result + id;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        long temp;
        temp = Double.doubleToLongBits(salary);
        result = prime * result + (int) (temp ^ (temp >>> 32));
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Employee other = (Employee) obj;
        if (age != other.age)
            return false;
        if (id != other.id)
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        if (Double.doubleToLongBits(salary) != Double.doubleToLongBits(other.salary))
            return false;
        return true;
    }

    @Override
    public String toString() {
        return "Employee [id=" + id + ", name=" + name + ", age=" + age + ", salary=" + salary + "]";
    }

}

步驟二:事先聲明一個(gè)emps員工集合

List<Employee> emps = Arrays.asList(
            new Employee(101, "張三", 18, 9999.99),
            new Employee(102, "李四", 59, 6666.66),
            new Employee(103, "王五", 28, 3333.33),
            new Employee(104, "趙六", 8, 7777.77),
            new Employee(105, "田七", 38, 5555.55)
    );

步驟三:測(cè)試實(shí)現(xiàn)要求

@Test
    public void test1(){
        Collections.sort(emps, (e1, e2) -> {
            if(e1.getAge() == e2.getAge()){
                    return e1.getName().compareTo(e2.getName());
            }else{
                return -Integer.compare(e1.getAge(), e2.getAge());
            }
        });
        
        for (Employee emp : emps) {
            System.out.println(emp);
        }
    }
2、(1)、聲明函數(shù)式接口,接口中聲明抽象方法,public String getValue(String str);

(2)、聲明類TestLambda,類中編寫方法使用接口作為參數(shù),將一個(gè)字符串轉(zhuǎn)換成大寫,并作為方法的返回值。

(3)、再將一個(gè)字符串的第2個(gè)和第4個(gè)索引位置進(jìn)行截取子串。

步驟一:先聲明一個(gè)函數(shù)式接口MyFunction,并接口中聲明抽象方法,public String getValue(String str);

@FunctionalInterface
public interface MyFunction {
    
    public String getValue(String str);

}

步驟二:編寫一個(gè)方法處理字符串,這個(gè)方法在Lambda類中定義的。

//需求:用于處理字符串
    public String strHandler(String str, MyFunction mf){
        return mf.getValue(str);
    }

步驟三:測(cè)試,實(shí)現(xiàn)具體的要求

@Test
    public void test2(){
    //這個(gè)是測(cè)試去除字符串的前后空格
        String trimStr = strHandler("\t\t\t 你是大傻逼   ", (str) -> str.trim());
        System.out.println(trimStr);
        
    //這個(gè)是測(cè)試將字符串轉(zhuǎn)換成大寫的
        String upper = strHandler("abcdef", (str) -> str.toUpperCase());
        System.out.println(upper);
        
    //截取字符串的指定索引的字符串
        String newStr = strHandler("我大望江縣威武", (str) -> str.substring(2, 5));
        System.out.println(newStr);
    }
3、(1)、聲明一個(gè)帶兩個(gè)泛型的函數(shù)式接口,泛型類型為<T,R>T為參數(shù),R為返回值。

(2)、接口中聲明對(duì)應(yīng)抽象方法

(3)、在TestLambda類中聲明方法,使用接口作為參數(shù),計(jì)算兩個(gè)long型參數(shù)的和。

(4)、在計(jì)算兩個(gè)long型參數(shù)的乘積。

步驟一:聲明一個(gè)帶兩個(gè)泛型的函數(shù)式接口,泛型類型為<T,R>T為參數(shù),R為返回值

public interface MyFunction2<T, R> {

    public R getValue(T t1, T t2);
    
}

步驟二:編寫一個(gè)方法,計(jì)算兩個(gè)long型的數(shù)據(jù)

//需求:對(duì)于兩個(gè) Long 型數(shù)據(jù)進(jìn)行處理
    public void op(Long l1, Long l2, MyFunction2<Long, Long> mf){
        System.out.println(mf.getValue(l1, l2));
    }

步驟三:測(cè)試,并實(shí)現(xiàn)要求。

@Test
    public void test3(){
    //實(shí)現(xiàn)兩個(gè)long型的數(shù)值的和
        op(100L, 200L, (x, y) -> x + y);
    //實(shí)現(xiàn)兩個(gè)long型的數(shù)值的積    
        op(100L, 200L, (x, y) -> x * y);
    }
    
?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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