java代碼之美(1)---Java8 Lambda

Lambda

 

1、概述

一、什麼是Lambda表達式

Lambda 表達式是一種匿名函數,簡單地說,它是沒有聲明的方法,也即沒有訪問修飾符、返回值聲明和名字。html

它能夠寫出更簡潔、更靈活的代碼。做爲一種更緊湊的代碼風格,使 Java 語言的表達能力獲得了提高。java

二、Lambda表達式的語法

基本語法:  (parameters) -> expressionexpress

     或者:(parameters) ->{ statements; 數組

舉例說明:app

// 1. 不須要參數,返回值爲 5  
() -> 5  
  
// 2. 接收一個參數(數字類型),返回其2倍的值  
x -> 2 * x  
  
// 3. 接受2個參數(數字),並返回他們的差值  
(x, y) -> x – y  
  
// 4. 接收2個int型整數,返回他們的和  
(int x, int y) -> x + y  
  
// 5. 接受一個 string 對象,並在控制檯打印,不返回任何值(看起來像是返回void)  
(String s) -> System.out.print(s)  

三、什麼是函數式接口

  再對上面進行舉例說明以前,必須先來理解下函數式接口,由於Lambda是創建在函數式接口的基礎上的。ide

 記住!
函數

   (1)只包含一個抽象方法的接口,稱爲函數式接口。post

  (2)你能夠經過 Lambda 表達式來建立該接口的對象。this

  (3)咱們能夠在任意函數式接口上使用 @FunctionalInterface 註解,這樣作能夠檢測它是不是一個函數式接口,同時 javadoc 也會包含一條聲明,說明這個接口是一個函數式接口。url

在實際開發者有兩個比較常見的函數式接口:Runnable接口,Comparator接口

   先舉例Runnable接口相關

public class Test {
    
    public static void main(String[] args) {
        
        // 1.1使用匿名內部類  
        new Thread(new Runnable() {  
            @Override  
            public void run() {  
                System.out.println("Hello world !");  
            }  
        }).start();  
          
        // 1.2使用 lambda 得到Runnable接口對象  
        new Thread(() -> System.out.println("Hello world !")).start();  
        
//=============================================================================
        
        // 2.1使用匿名內部類  
        Runnable race1 = new Runnable() {  
            @Override  
            public void run() {  
                System.out.println("Hello world !");  
            }  
        };  
          
        // 2.2使用 lambda直接得到接口對象 
        Runnable race2 = () -> System.out.println("Hello world !");          
        
        // 直接調用 run 方法(沒開新線程哦!)  
        race1.run();  
        race2.run();  
    }
}
/*輸出結果
 * Hello world !
 * Hello world !
 * Hello world !
 * Hello world !
 */

經過上面案例能夠看出:經過Lambda表達式看去舒服清爽多了,2而經過匿名內部類代碼老是不夠整潔。

再舉一個例子:使用Lambda對數組排序

public class TestArray {
    
    public static void main(String[] args) {
        String[] players = {"zhansgan", "lisi", "wangwu", "zhaoliu",  "wangmazi"};  

        // 1.1 使用匿名內部類根據 surname 排序 players  
        Arrays.sort(players, new Comparator<String>() {  
            @Override  
            public int compare(String s1, String s2) {  
                return (s1.compareTo(s2));  
            }  
        });  
        
        // 1.2 使用 lambda 排序,根據 surname  
        Arrays.sort(players, (String s1, String s2) ->  s1.compareTo(s2));  
         
//================================================================================================
          
        // 2.1 使用匿名內部類根據 name lenght 排序 players  
        Arrays.sort(players, new Comparator<String>() {  
            @Override  
            public int compare(String s1, String s2) {  
                return (s1.length() - s2.length());  
            }  
        });  

        // 2.2使用Lambda,根據name length  
        Arrays.sort(players, (String s1, String s2) -> (s1.length() - s2.length()));  
    
//==================================================================================================    
        
        // 3.1 使用匿名內部類排序 players, 根據最後一個字母  
        Arrays.sort(players, new Comparator<String>() {  
            @Override  
            public int compare(String s1, String s2) {  
                return (s1.charAt(s1.length() - 1) - s2.charAt(s2.length() - 1));  
            }  
        });  

        // 3.2 使用Lambda,根據最後一個字母
        Arrays.sort(players, (String s1, String s2) -> (s1.charAt(s1.length() - 1) - s2.charAt(s2.length() - 1)));  
    }
}

經過上面例子咱們再來思考爲何Lambda表達式須要函數式接口?其實很簡單目的就是爲來保證惟一。

你的Runnable接口只要一個抽象方法,那麼我用() -> System.out.println("Hello world !"),就只能表明run方法,若是你下面還有一個抽象方法,那我使用Lambda表達式,

那鬼才知道要調用哪一個抽象方法呢。

 

2、方法引用

一、基本介紹

首先注意:方法引用,不是方法調用!方法引用,不是方法調用!方法引用,不是方法調用!

函數式接口的實例能夠經過 lambda 表達式、 方法引用、構造方法引用來建立。方法引用是 lambda 表達式的語法糖,任何用方法引用的地方均可由lambda表達式替換,

可是並非全部的lambda表達式均可以用方法引用來替換。

舉例

這就是一個打印集合全部元素的例子,value -> System.out.println(value) 是一個Consumer函數式接口, 這個函數式接口能夠經過方法引用來替換。

public class TestArray {
    
    public static void main(String[] args) {
         List<String> list = Arrays.asList("xuxiaoxiao", "xudada", "xuzhongzhong");
           list.forEach(value -> System.out.println(value));
    }
    /* 輸出:
     * xuxiaoxiao
     * xudada
     * xuzhongzhong
     */
}

使用方法引用的方式,和上面的輸出是同樣的,方法引用使用的是雙冒號(::)

list.forEach(System.out::println);

二、分類

類別 使用形式
靜態方法引用 類名 :: 靜態方法名
實例方法引用 對象名(引用名) :: 實例方法名
類方法引用 類名 :: 實例方法名
構造方法引用 類名 :: new

 

 

 

 

 

 

(1)靜態方法引用

public class Apple {

    private String name;
    private String color;
    private double weight;

    public Apple(String name, String color, double weight) {
        this.name = name;
        this.color = color;
        this.weight = weight;
    }

    public static int compareByWeight(Apple a1, Apple a2) {
        double diff = a1.getWeight() - a2.getWeight();
        return new Double(diff).intValue();
    }

    //還有getter setter toString
}    

有一個蘋果的List,如今須要根據蘋果的重量進行排序。List 的 sort 函數接收一個 Comparator 類型的參數,Comparator 是一個函數式接口,接收兩個參數,

返回一個int值。Apple的靜態方法compareByWeight正好符合Comparator函數式接口,因此可使用:

Apple::compareByWeight 靜態方法引用來替代lambda表達式

public class LambdaTest {

    public static void main(String[] args) {

        Apple apple1 = new Apple("紅富士", "Red", 280);
        Apple apple2 = new Apple("馮心", "Yello", 470);
        Apple apple3 = new Apple("大牛", "Red", 320);
        Apple apple4 = new Apple("小小", "Green", 300);

        List<Apple> appleList = Arrays.asList(apple1, apple2, apple3, apple4);

        //lambda 表達式形式
        //appleList.sort((Apple a1, Apple a2) -> {
        //    return new Double(a1.getWeight() - a2.getWeight()).intValue();
        //});

        //靜態方法引用形式(能夠看出引用方法比上面的更加簡單
        appleList.sort(Apple::compareByWeight);

        appleList.forEach(apple -> System.out.println(apple));

    }
}
輸出:
Apple{category='紅富士', color='Red', weight=280.0}
Apple{category='小小', color='Green', weight=300.0}
Apple{category='大牛', color='Red', weight=320.0}
Apple{category='馮心', color='Yello', weight=470.0}

注意:Apple.compareByWeight是方法的調用,而Apple::compareByWeight方法引用,這二者徹底不是一回事。

(2)實例方法引用

這個compareByWeight是一個實例方法

public class AppleComparator {

    public int compareByWeight(Apple a1, Apple a2) {
        double diff = a1.getWeight() - a2.getWeight();
        return new Double(diff).intValue();
    }
}

下面的例子經過實例對象的方法引用 comparator::compareByWeight 來代替lambda表達式

public class LambdaTest {

    public static void main(String[] args) {

        Apple apple1 = new Apple("紅富士", "Red", 280);
        Apple apple2 = new Apple("馮心", "Yello", 470);
        Apple apple3 = new Apple("哈哈", "Red", 320);
        Apple apple4 = new Apple("小小", "Green", 300);


        List<Apple> appleList = Arrays.asList(apple1, apple2, apple3, apple4);

        //lambda 表達式形式
        //appleList.sort((Apple a1, Apple a2) -> {
        //    return new Double(a1.getWeight() - a2.getWeight()).intValue();
        //});

        //實例方法引用
        AppleComparator comparator = new AppleComparator();
        appleList.sort(comparator::compareByWeight);

        appleList.forEach(apple -> System.out.println(apple));

    }
}
輸出:
Apple{category='紅富士', color='Red', weight=280.0}
Apple{category='小小', color='Green', weight=300.0}
Apple{category='哈哈', color='Red', weight=320.0}
Apple{category='馮心', color='Yello', weight=470.0}

經過上面兩個例子能夠看到,靜態方法引用和實例方法引用都是比較好理解的。

(3)類方法引用

通常來講,同類型對象的比較,應該當前調用方法的對象與另一個對象進行比較,好的設計應該像下面: 

public class Apple {

    private String category;
    private String color;
    private double weight;

    public Apple(String category, String color, double weight) {
        this.category = category;
        this.color = color;
        this.weight = weight;
    }
//這裏和上面靜態方式惟一區別就是這個參數就一個,須要實例對象調這個方法
    public int compareByWeight(Apple other) {
        double diff = this.getWeight() - other.getWeight();
        return new Double(diff).intValue();
    }

    //getter setter toString
}

    仍是以前List排序的例子,看看使用類方法引用如何寫:

public class LambdaTest {

    public static void main(String[] args) {

        Apple apple1 = new Apple("紅富士", "Red", 280);
        Apple apple2 = new Apple("黃元帥", "Yello", 470);
        Apple apple3 = new Apple("紅將軍", "Red", 320);
        Apple apple4 = new Apple("國光", "Green", 300);


        List<Apple> appleList = Arrays.asList(apple1, apple2, apple3, apple4);

        //lambda 表達式形式
        //appleList.sort((Apple a1, Apple a2) -> {
        //    return new Double(a1.getWeight() - a2.getWeight()).intValue();
        //});

        //這裏是類方法引用
        appleList.sort(Apple::compareByWeight);

        appleList.forEach(apple -> System.out.println(apple));

    }
}
輸出:
Apple{category='紅富士', color='Red', weight=280.0}
Apple{category='國光', color='Green', weight=300.0}
Apple{category='紅將軍', color='Red', weight=320.0}
Apple{category='黃元帥', color='Yello', weight=470.0}

     這裏使用的是:類名::實例方法名。首先要說明的是,方法引用不是方法調用。compareByWeight必定是某個實例調用的,就是lambda表達式的第一個參數,而後lambda

表達式剩下的參數做爲 compareByWeight的參數,這樣compareByWeight正好符合lambda表達式的定義。

或者也能夠這樣理解:

(Apple a1, Apple a2) -> { return new Double(a1.getWeight() - a2.getWeight()).intValue(); }

int compareByWeight(Apple other) 須要當前對象調用,而後與另一個對象比較,而且返回一個int值。能夠理解爲lambda表達式的第一個參數 a1 賦值給當前對象, 而後 a2

賦值給 other對象,而後返回int值。

(4)構造方法引用

public class ConstructionMethodTest {

    public String getString(Supplier<String> supplier) {
        return supplier.get();
    }

    public static void main(String[] args) {

        ConstructionMethodTest test = new ConstructionMethodTest();

        //lambda表達式形式
        System.out.println(test.getString(() -> { return new String();}));

        //構造方法引用形式
        System.out.println(test.getString(String::new));

    }
}

getString 方法接收一個Supplier類型的參數,Supplier 不接收參數,返回一個String。lambda表達式應該這樣寫:

() -> { return new String();}

替換成方法引用的形式以下: 實際上調用的是String 無參構造方法。

String::new

 

參考

    一、Java中Lambda表達式的使用

    二、java8特性概要以及demo分析

    3java8 筆記 - 方法引用(四)

 

想太多,作太少,中間的落差就是煩惱。想沒有煩惱,要麼別想,要麼多作。中校【9】 

相關文章
相關標籤/搜索