?Java8新特性之Lambda表示式,函式式介面,方法引用和default關鍵字

六脈神劍發表於2019-12-25

前言

文字已收錄至我的GitHub倉庫,歡迎Star:github.com/bin39232820…
種一棵樹最好的時間是十年前,其次是現在
我知道很多人不玩qq了,但是懷舊一下,歡迎加入六脈神劍Java菜鳥學習群,群聊號碼:549684836 鼓勵大家在技術的路上寫部落格

絮叨

今天 開始寫Java8新特性系列,怎麼說呢,主要有幾個新東西

  • Lambda表示式
  • 函式式介面
  • 方法引用
  • Stream流
  • Optionl類
  • default關鍵字

這個四個的主要作用 簡化程式碼編寫,提高效能等等,但是也會給維護帶來麻煩,因為不懂的人去看,真心累,但是寫起來是真的香,今天打算講標題上的。 考慮大家可能對 內部類 和 泛型不熟悉,有需要的可以看我這2篇文章
?你不知道的Java內部類
?你必須知道的Java泛型

文字力求簡單講清每個知識點,希望大家看完能有所收穫

Lambda表示式簡介

Lambda 表示式,也可稱為閉包,允許把函式作為一個引數,使程式碼更簡潔。

那麼什麼是函數語言程式設計呢?

首先,什麼是函數語言程式設計,引用廖雪峰先生的教程裡面的解釋就是說:函數語言程式設計就是一種抽象程度很高的程式設計正規化,純粹的函數語言程式設計語言編寫的函式沒有變數,因此,任意一個函式,只要輸入是確定的,輸出就是確定的,這種純函式我們稱之為沒有副作用。而允許使用變數的程式設計語言,由於函式內部的變數狀態不確定,同樣的輸入,可能得到不同的輸出,因此,這種函式是有副作用的。函數語言程式設計的一個特點就是,允許把函式本身作為引數傳入另一個函式,還允許返回一個函式!

基本語法

// 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)
複製程式碼

這個幾個基本語法 和下面的4種函式式介面是對應的。謝謝讀者的提醒。本來還沒把這塊加上去,新手看起來有點懵逼。

基礎列子

建立執行緒

?Java8新特性之Lambda表示式,函式式介面,方法引用和default關鍵字

再來看看遍歷Map集合:

?Java8新特性之Lambda表示式,函式式介面,方法引用和default關鍵字
引入lambda表示式的一個最直觀的作用就是大大的簡化了程式碼的開發,像其他一些程式語言Scala,Python等都是支援函式式的寫法的。當然,不是所有的介面都可以通過這種方法來呼叫,只有函式式介面才行,jdk1.8裡面定義了好多個函式式介面,我們也可以自己定義一個來呼叫,下面說一下什麼是函式式介面。

函式式介面

  • 只包含一個抽象方法的介面,稱為函式式介面。
  • 可以通過 Lambda 表示式來建立該介面的物件。(若 Lambda 表示式丟擲一個受檢異常,那麼該異常需要在目標介面的抽象方 法上進行宣告)。
  • 可以在任意函式式介面上使用 @FunctionalInterface 註解,這樣做可以檢查它是否是一個函式式介面,同時 javadoc 也會包 含一條宣告,說明這個介面是一個函式式介面。
  • 之所以Lambda必須和函式式介面配合是因為,介面如果多個函式,則Lambda表示式無法確定實現的是哪個

來個簡單的例子

這是一個自定義的函式式介面:作用是傳入一個引數,返回一個引數。

@FunctionalInterface
public interface MyNumber<T> {
    T getValue(T t);
}
複製程式碼

這是用法


    public static void main(String[] args) {

        //Lambda的寫法
        System.out.println(toUpperString(str -> str.toUpperCase(), "abc")); //ABC
        //匿名內部類的寫法
        System.out.println(toUpperString(new MyNumber<String>() {
            @Override
            public String getValue(String s) {
                return s;
            }
        }, "abc"));

    }

    public static String toUpperString(MyNumber<String> mn, String str) {
        return mn.getValue(str);
    }
複製程式碼

作為引數傳遞 Lambda 表示式:為了將 Lambda 表示式作為引數傳遞,接 收Lambda 表示式的引數型別必須是與該 Lambda 表示式相容的函式式介面 的型別。

Java 內建四大核心函式式介面

函式式介面 引數型別 返回型別 用途
Consumer 消費型介面 T void 對型別為T的物件應用操 作,包含方法: void accept(T t)
Supplier 供給型介面 T 返回型別為T的物件,包 含方法:T get();
Function<T, R> 函式型介面 T R 對型別為T的物件應用操 作,並返回結果。結果 是R型別的物件。包含方 法:R apply(T t);
Predicate 斷言型介面 T boolean 確定型別為T的物件是否 滿足某約束,並返回 boolean 值。包含方法 boolean test(T t);

使用場景

之前MyNumber這種介面配合Lambda使用,可以發現必須先宣告介面,很麻煩,而內建的幾個介面就是解決這種問題的;而這些內建的介面也存在大量的內部實現,或者程式設計者自己定義的類,只要符合對應的引數型別和返回值型別的,都可以使用。例如:定義MyClass只要符合引數T返回R,則都可以使用Function<T, R> 函式型介面對應形式,包含下面的構造器引用,方法引用等等形式

如下:

    public static void main(String[] args) {

        //Lambda的寫法
        System.out.println(toUpperString1(str -> str.toUpperCase(), "abc")); //ABC
        //匿名內部類的寫法
        System.out.println(toUpperString1(new MyNumber<String>() {
            @Override
            public String getValue(String s) {
                return s;
            }
        }, "abc"));

        //使用內建的函式式介面的lambda寫法
        System.out.println(toUpperString(str->str.toUpperCase(),"abc"));
    }


    //內建的函式介面
    public static String toUpperString(Function<String,String> mn, String str) {
        return mn.apply(str);
    }
    //定義的函式介面
    public static String toUpperString1(MyNumber<String> mn, String str) {
        return mn.getValue(str);
    }
複製程式碼

方法引用與構造器引用

方法引用

當要傳遞給Lambda體的操作,已經有實現的方法了,可以使用方法引用! (實現抽象方法的引數列表,必須與方法引用方法的引數列表保持一致!) 方法引用:使用操作符 “::” 將方法名和物件或類的名字分隔開來。 如下三種主要使用情況:

  • 物件::例項方法
  • 類::靜態方法
  • 類::例項方法

例子

        //例如:此時Consumer引數型別和返回值和println方法一致
        //物件::例項方法名
        //為什麼這樣用,因為 ConSumer這個函式介面就是傳入引數,返回為void ,並且printl 方法就是這樣的
        /**我們看下面的原始碼 和那個函式介面一樣
         *     public void println(String x) {
         *         synchronized (this) {
         *             print(x);
         *             newLine();
         *         }
         *     }
         */
        
        PrintStream printStream=System.out;
        Consumer<String> con= printStream::println;
        con.accept("haha");
        
        /類::靜態方法名
    Comparator<Integer> com=Integer::compare;
    Comparator<Integer> com1=(x,y)->Integer.compare(x,y);
複製程式碼

構造器引用

格式: ClassName::new

與函式式介面相結合,自動與函式式介面中方法相容。 可以把構造器引用賦值給定義的方法,與構造器引數 列表要與介面中抽象方法的引數列表一致

 Function<Integer,MyClass> fun= n->new MyClass(1);
        Function<Integer,MyClass> fun1=MyClass::new ;
        MyClass apply = fun1.apply(1);
複製程式碼

如果存在多個構造器,會自動匹配對應引數或者無參的構造器,取決於apply傳遞的引數。

default關鍵字

 在java裡面,我們通常都是認為介面裡面是隻能有抽象方法,不能有任何方法的實現的,那麼在jdk1.8裡面打破了這個規定,引入了新的關鍵字default,通過使用default修飾方法,可以讓我們在介面裡面定義具體的方法實現,如下  

public interface NewCharacter {
    
    public void test1();
    
    public default void test2(){
        System.out.println("我是新特性1");
    }

}
複製程式碼

那這麼定義一個方法的作用是什麼呢?為什麼不在介面的實現類裡面再去實現方法呢?

其實這麼定義一個方法的主要意義是定義一個預設方法,也就是說這個介面的實現類實現了這個介面之後,不用管這個default修飾的方法,也可以直接呼叫,如下。

public class NewCharacterImpl implements NewCharacter{

    @Override
    public void test1() {
        
    }
    
    public static void main(String[] args) {
        NewCharacter nca = new NewCharacterImpl();
        nca.test2();
    }

}
複製程式碼

所以說這個default方法是所有的實現類都不需要去實現的就可以直接呼叫,那麼比如說jdk的集合List裡面增加了一個sort方法,那麼如果定義為一個抽象方法,其所有的實現類如arrayList,LinkedList等都需要對其新增實現,那麼現在用default定義一個預設的方法之後,其實現類可以直接使用這個方法了,這樣不管是開發還是維護專案,都會大大簡化程式碼量。

結尾

好了,今天就講那麼多了。謝謝大家的支援

日常求贊

好了各位,以上就是這篇文章的全部內容了,能看到這裡的人呀,都是真粉

創作不易,各位的支援和認可,就是我創作的最大動力,我們下篇文章見

六脈神劍 | 文 【原創】如果本篇部落格有任何錯誤,請批評指教,不勝感激 !

相關文章