1. 程式人生 > 其它 >Java8新特性lambda表示式快速入門

Java8新特性lambda表示式快速入門

目錄

隨著Java語言的不斷髮展,Java8提供的新特性lambda表示式也成為越來越多開發者喜歡的寫法,為了順應時代的變化,需要好好地學習這種寫法,並應用在平時的開發中。我們先從簡單的例子著手,一步步的深入到lambda的複雜寫法,首先我們的任務是建立一個用來比較兩個int值大小的介面,並給它新增具體的實現方法,然後進行呼叫

。介面定義如下:

interface Comparator{
    int compare(int a,int b);
}

常規寫法

寫法一:新建類並實現該介面

建立MyComparator.java並實現Comparator介面:

class MyComparator implements Comparator{
    @Override
    public int compare(int a, int b) {
        return a > b ? a : b; //輸出最大的值
    }
}

然後進行呼叫:

public static void main(String[] args) {
    Comparator comparator = new MyComparator();
    
    int res = comparator.compare(1, 10);
    System.out.println(res);
}

寫法二:使用匿名函式

直接new介面

public static void main(String[] args) {
    //使用匿名方法實現
    Comparator comparator = new Comparator() {
        @Override
        public int compare(int a, int b) {
            return a > b ? a : b;
        }
    };
    
    int res = comparator.compare(1, 10);
    System.out.println(res);
}

lambda寫法

跟上面的常規寫法不同的是,lambda只關注方法的入參方法體中具體實現的邏輯,其餘能省的程式碼全部都可以省略,我們先看一下第一種寫法:

寫法一:lambda常規寫法

自己比較一下跟匿名函式寫法的差別,是不是非常的簡潔,lambda只聲明瞭入參方法體內的邏輯,關鍵地方在於入參和方法體之間使用了->箭頭符號進行連線,箭頭符號讀作gose to

public static void main(String[] args) {
    //簡單的lambda寫法
    Comparator comparator = (int a,int b) -> {
        return a > b ? a : b;
    };
    
    int res = comparator.compare(1, 10);
    System.out.println(res);
}

到這裡你應該有一個基本的印象了,這一步轉變非常重要,停下來好好思考一下。

寫法二:lambda簡寫

仔細觀察可以發現寫法一中仍然有多餘的程式碼,比如說我們在之前的介面中已經宣告過一次入參的型別,在使用的時候又聲明瞭一次,這顯然是多餘的寫法。其實上面的寫法依然可以更簡潔,只要滿足下面這幾點,都可以省略。

  1. 當入參型別和初始定義型別一致時,可以省略入參型別。
  2. 當方法體中只有一句程式碼時,可以省略大括號。
  3. 當方法體中只有一句程式碼,並且需要返回值時,可以省略return關鍵字。

更簡單的寫法如下:

public static void main(String[] args) {
    //省略了入參型別、大括號、return關鍵字
    Comparator comparator = (a,b) -> a > b ? a : b;
    
    int res = comparator.compare(1, 10);
    System.out.println(res);
}

中場疑問

上面我們定義的介面只有一個抽象方法compare(),有的同學就會有疑問,那如果有多個抽象方法怎麼寫呢?問的很好!由於lambda的寫法非常的簡潔,但這是在一定的條件限制下才能這麼寫,其中非常重要的一點就是,只有一個抽象方法的接口才支援lambda的寫法,這種介面被稱為函式式介面,可以通過@FunctionalInterface註解進行修飾。

@FunctionalInterface只修飾函式式介面,即只有一個抽象方法的介面。

lambda的方法引用

寫法一

lambda表示式中方法體只有一行程式碼才能有最簡單的寫法,那如果我想寫一些複雜的業務就不能簡寫了嗎?其實並不是的,這時候我們就可以泳道lambda的方法引用,將較複雜的業務封裝成方法,然後在lambda中進行呼叫,假如還是上面的例子,我們可以將方法體中的那一行程式碼封裝成一個方法:

public static int max(int a,int b){
    //這裡可以寫一些較為複雜的業務
    return a > b ? a : b;
}

這時候lambda表示式就可以這麼寫:

public static void main(String[] args) {
    //方法體中引用max函式
    Comparator comparator = (a,b) -> max(a,b);
    int res = comparator.compare(1, 10);
    System.out.println(res);
}

上面就是一個普通的lambda方法引用,簡單的說就是將方法體中的程式碼封裝成一個一方法,然後在呼叫這個方法即可,需要注意的是max函式中的返回值必須要跟介面中的抽象方法一致,否則就會報異常。

寫法二

上面的寫法雖然簡單有效,但依然有多餘的地方,那就是方法引數重複了,程式碼(a,b) -> max(a,b);中出現了兩次(a,b),在實際開發中,我們一般採用另一種寫法來代替->箭頭符號,請大家牢記語法:

方法的隸屬者::方法名。

隸屬者可以理解為,能夠呼叫這個方法的物件,如果該方法是static修飾的,則隸屬者位當前類;如果不是static修飾的,則隸屬者屬於一個物件。如果聽不懂也沒關係,看一個栗子就清晰明瞭。

首先看一下完整的程式碼:

//定義介面
interface Comparator {
    int compare(int a, int b);
}

public class Program {
    public static void main(String[] args) {
        //使用lambda的方法引用呼叫max方法,隸屬者為類
		Comparator comparator = Program::max;
        int res = comparator.compare(1, 10);
        System.out.println(res);
    }
    //用static修飾的方法
    public static int max(int a,int b){
        return a > b ? a : b;
    }
}

從上面程式碼中我們發現,將之前的(a,b) -> max(a,b);寫成了Program::max;,因為方法max被關鍵字static修飾了,因此隸屬者應該是Program這個類。也可以發現省略了引數(a,b),這麼寫就已經是非常的簡潔了,但這麼寫需要滿足兩個基本條件:

  1. 只有介面中抽象方法的引數格式跟定義的方法引數格式一樣,才能省略引數。
  2. 介面中抽象方法的返回值必須跟定義的方法的返回值一樣。

這就是java8引入的新特性,一切為了簡潔明瞭。

方法不是static怎麼寫?

理解了上面,這個就比較好懂了,貼一下程式碼,不做解釋了

public class Program {
    public static void main(String[] args) {
        //建立一個Program物件
        Program p = new Program();
        //使用lambda的方法引用呼叫max方法,隸屬者為物件
		Comparator comparator = p::max;
        int res = comparator.compare(1, 10);
        System.out.println(res);
    }
    //沒有被static修飾的方法
    public int max(int a,int b){
        return a > b ? a : b;
    }
}

練習題

素材如下:

Person.java

class Person{
    String name ;
    int age;

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

準備集合

public class Program {
    public static void main(String[] args) {
        List<Person> list = new ArrayList<>();
        list.add(new Person("張三",18));
        list.add(new Person("李四",30));
        list.add(new Person("王五",16));
        list.add(new Person("趙六",20));
        list.add(new Person("田七",40));
        list.add(new Person("馬八",25));        
        printList(list);
    }
    
    //列印集合
    public static void printList(List<Person> list){
        for (Person person : list) {
            System.out.println(person);
        }
    }
}

1. 將集合按照指定屬性進行排序

按照person的年齡進行從低到高排序

普通的排序方式:

//使用正常的方式排序
list.sort(new Comparator<Person>() {
    @Override
    public int compare(Person o1, Person o2) {
        return o1.getAge() - o2.getAge();
    }
});

問題:你能將普通的排序方式改造成lambda的方式嗎?寫出來後思考還有其他寫法嗎?

2. 將list.forEach方法改造成lambda方式

正常的list遍歷:

list.forEach(new Consumer<Person>() {
    @Override
    public void accept(Person person) {
		System.out.println(person.toString());
    }
});

問題:如何改造成lambda的方式??改造之後修改練習1中的遍歷程式碼

3.刪除集合中指定元素

刪除年齡大於35的人

正常的刪除:

list.removeIf(new Predicate<Person>() {
    @Override
    public boolean test(Person person) {
        return person.age > 35;
    }
});

問題:如何改造成lambda的方式??

答案

  1. 排序(二選一)
list.sort((p1,p2) -> p1.getAge() - p2.getAge());
Collections.sort(list,(p1,p2) -> p1.getAge() - p2.getAge());
  1. 遍歷
list.forEach(person -> System.out.println(person.toString()));
  1. 刪除指定元素
list.removeIf(person -> person.age > 35);