天天看點

Lambada表達式之初入江湖

1.1回顧匿名内部類

要啟動一個線程,需要建立一個 Thread 類的對象并調用 start 方法。而為了指定線程執行的内容,需要調用 Thread 類的構造方法:

public Thread(Runnable target)

為了擷取 Runnable 接口的實作對象,可以為該接口定義一個實作類 RunnableImpl :

public class RunnableImpl implements Runnable {
 @Override 
      public void run() {
            System.out.println("多線程任務執行!");
      }
}
           

然後建立該實作類的對象作為 Thread 類的構造參數:

public class Demo03ThreadInitParam {
    public static void main(String[] args) {
          Runnable task = new RunnableImpl();
             new Thread(task).start();
    }
}
           

1.2使用匿名内部類

這個 RunnableImpl 類隻是為了實作 Runnable 接口而存在的,而且僅被使用了唯一一次,是以使用匿名内部類的 文法即可省去該類的單獨定義,即匿名内部類:

public class Demo04ThreadNameless {
 public static void main(String[] args) {
    new Thread(new Runnable() {
       @Override
       public void run() {
          System.out.println("多線程任務執行!");
       }
   }).start();
 }
}
           

一方面,匿名内部類可以幫我們省去實作類的定義;另一方面,匿名内部類的文法——确實太複雜了!

函數式程式設計重點在函數上,在數學中,函數就是有輸入量、輸出量的一套計算方案,也就是“拿什麼東西做什麼事情 最後有什麼結果”。相對而言,面向對象過分強調“必須通過對象的形式來做事情”.

比如: 我要計算: y = 3*x+4,其中x就是輸入量 3x+4就是做什麼,最後的y就是計算的結果

而函數式思想是盡量忽略面向對象的複雜文法——強調做什麼,而不是以什麼形式做。

1.3 備援的Runnable代碼

對于 Runnable 的匿名内部類用法,可以分析出幾點内容:

  1. Thread 類需要 Runnable 接口作為參數,其中的抽象 run 方法是用來指定線程任務内容的核心;
  2. 為了指定 run 的方法體,不得不需要 Runnable 接口的實作類;
  3. 為了省去定義一個 RunnableImpl 實作類的麻煩,不得不使用匿名内部類;
  4. 必須覆寫重寫抽象 run 方法,是以方法名稱、方法參數、方法傳回值不得不再寫一遍,且不能寫錯;
  5. 而實際上,似乎隻有方法體才是關鍵所在。

1.4 體驗Lambda的更優寫法

借助Java 8的全新文法,上述 Runnable 接口的匿名内部類寫法可以通過更簡單的Lambda表達式達到等效:

public class Demo02LambdaRunnable { 
     public static void main(String[] args) {
       new Thread(() ‐> System.out.println("多線程任務執行!")).start(); // 啟動線程
     }
}
           

這段代碼和剛才的執行效果是完全一樣的,可以在1.8或更高的編譯級别下通過。從代碼的語義中可以看出:我們 啟動了一個線程,而線程任務的内容以一種更加簡潔的形式被指定。

不再有“不得不建立接口對象”的束縛,不再有“抽象方法覆寫重寫”的負擔,就是這麼簡單! Lambda是怎樣擊敗面向對象的?在上例中,核心代碼其實隻是如下所示的内容:

1.5 語義分析

仔細分析該代碼中的語義, Runnable 接口隻有一個 run 方法的定義:

public abstract void run();

即制定了一種做事情的方案(其實就是一個函數):

無參數:不需要任何條件即可執行該方案。

無傳回值:該方案不産生任何結果。

代碼塊(方法體):該方案的具體執行步驟。

前面的一對小括号即 run 方法的參數(無),代表做這件事不需要參數;

中間的一個箭頭代表将前面的參數傳遞給後面的代碼;

後面的輸出語句即業務邏輯代碼。

1.6 Lambda标準格式

Lambda省去面向對象的條條框框,格式由3個部分組成:

一些參數

一個箭頭

一段代碼

Lambda表達式的标準格式為:

(參數類型 參數名稱) ‐> { 代碼語句;return 值; }

格式說明:

小括号内的文法與傳統方法參數清單一緻:無參數則留白;多個參數則用逗号分隔。

—> 是新引入的文法格式,代表指向動作(做什麼事)。

大括号内的文法與傳統方法體要求基本一緻

1.7 示例:使用Lambda标準格式(無參無傳回)

給定一個廚子 Cook 接口,内含唯一的抽象方法 makeFood ,且無參數、無傳回值。如下:

public interface Cook {
     void makeFood();
}

public class Demo05InvokeCook {
     public static void main(String[] args) {
       invokeCook(() ‐> { System.out.println("吃飯啦!");});
     }
     private static void invokeCook(Cook cook) { 
          cook.makeFood();
     }
}
           
備注:小括号代表 Cook 接口 makeFood 抽象方法的參數為空,大括号代表 makeFood 的方法體。 
           

1.8 Lambda的參數和傳回值

下面舉例示範 java.util.Comparator 接口的使用場景代碼,其中的抽象方法定義為:

public abstract int compare(T o1, T o2);

當需要對一個對象數組進行排序時, Arrays.sort 方法需要一個 Comparator 接口執行個體來指定排序的規則。假設有 一個 Person 類,含有 String name 和 int age 兩個成員變量:

public class Person {
      private String name;
      private int age;
      // 省略構造器、toString方法與Getter Setter
}
           

傳統寫法

如果使用傳統的代碼對 Person[] 數組進行排序,寫法如下:

import java.util.Arrays;
import java.util.Comparator;
public class Demo06Comparator {
   public static void main(String[] args) {
                 // 本來年齡亂序的對象數組
           Person[] array = {
                    new Person("古力娜紮", 19),
                    new Person("迪麗熱巴", 18),
                    new Person("馬爾紮哈", 20) };
   // 匿名内部類
   Comparator<Person> comp = new Comparator<Person>(
       @Override 
      public int compare(Person o1, Person o2) { 
      return o1.getAge() ‐ o2.getAge(); 
      } 
   };
    Arrays.sort(array, comp); 
   // 第二個參數為排序規則,即Comparator接口執行個體
   for (Person person : array) {  
        System.out.println(person);
   }
  }
}
           

這種做法在面向對象的思想中,似乎也是“理所當然”的。其中 Comparator 接口的執行個體(使用了匿名内部類)代表 了“按照年齡從小到大”的排序規則。

代碼分析

下面我們來搞清楚上述代碼真正要做什麼事情。

為了排序, Arrays.sort 方法需要排序規則,即 Comparator 接口的執行個體,抽象方法 compare 是關鍵;

為了指定 compare 的方法體,不得不需要 Comparator 接口的實作類;

為了省去定義一個 ComparatorImpl 實作類的麻煩,不得不使用匿名内部類;

必須覆寫重寫抽象 compare 方法,是以方法名稱、方法參數、方法傳回值不得不再寫一遍,且不能寫錯;

實際上,隻有參數和方法體才是關鍵。

Lambda寫法

Arrays.sort(array, (Person a, Person b) ‐> { 
      return a.getAge() ‐ b.getAge();
});
           

1.9 示例:使用Lambda标準格式(有參有傳回)

給定一個電腦 Calculator 接口,内含抽象方法 calc 可以将兩個int數字相加得到和值:

public interface Calculator {
     int calc(int a, int b);
}
           

标準格式調用 invokeCalc 方法,完成120和130的相加計算:

public class Demo08InvokeCalc {
     public static void main(String[] args) {
       invokeCalc(120, 130, (int a, int b) ‐> {
         return a + b;  }); 
     }
     private static void invokeCalc(int a, int b, Calculator calculator) {
            int result = calculator.calc(a, b);
            System.out.println("結果是:" + result);
     }
}
           
備注:小括号代表 Calculator 接口 calc 抽象方法的參數,大括号代表 calc 的方法體。 
           

3.10 Lambda省略格式

可推導即可省略

Lambda強調的是“做什麼”而不是“怎麼做”,是以凡是可以根據上下文推導得知的資訊,都可以省略。例如上例還可 以使用Lambda的省略寫法:

public static void main(String[] args) {
    invokeCalc(120, 130, (a, b) ‐> a + b);
}
           

省略規則

在Lambda标準格式的基礎上,使用省略寫法的規則為:

  1. 小括号内參數的類型可以省略;
  2. 如果小括号内有且僅有一個參,則小括号可以省略;
  3. 如果大括号内有且僅有一個語句,則無論是否有傳回值,都可以省略大括号、return關鍵字及語句分号。

3.11 Lambda的使用前提

Lambda的文法非常簡潔,完全沒有面向對象複雜的束縛。但是使用時有幾個問題需要特别注意:

  1. 使用Lambda必須具有接口,且要求接口中有且僅有一個抽象方法。 無論是JDK内置的 Runnable 、 Comparator 接口還是自定義的接口,隻有當接口中的抽象方法存在且唯一時,才可以使用Lambda。
  2. 使用Lambda必須具有上下文推斷。 也就是方法的參數或局部變量類型必須為Lambda對應的接口類型,才能使用Lambda作為該接口的執行個體。
    備注:有且僅有一個抽象方法的接口,稱為“函數式接口”。
               

以下介紹函數式接口

1.1概念

函數式接口在Java中是指:有且僅有一個抽象方法的接口。 函數式接口,即适用于函數式程式設計場景的接口。而Java中的函數式程式設計展現就是Lambda,是以函數式接口就是可 以适用于Lambda使用的接口。隻有確定接口中有且僅有一個抽象方法,Java中的Lambda才能順利地進行推導。

備注:“文法糖”是指使用更加友善,但是原理不變的代碼文法。例如在周遊集合時使用的for-each文法,其實 底層的實作原理仍然是疊代器,這便是“文法糖”。從應用層面來講,Java中的Lambda可以被當做是匿名内部 類的“文法糖”,但是二者在原理上是不同的。

1.2函數式接口的定義

與 @Override 注解的作用類似,Java 8中專門為函數式接口引入了一個新的注解: @FunctionalInterface 。該注 解可用于一個接口的定義上:

import java.lang.FunctionalInterface;

@FunctionalInterface
public interface MyFunctionalInterface {
    void myMethod();
}
           

一旦使用該注解來定義接口,編譯器将會強制檢查該接口是否确實有且僅有一個抽象方法,否則将會報錯。需要注 意的是,即使不使用該注解,隻要滿足函數式接口的定義,這仍然是一個函數式接口,使用起來都一樣。

1.3自定義函數式接口(無參無傳回)

對于剛剛定義好的 MyFunctionalInterface 函數式接口,典型使用場景就是作為方法的參數:

public class FunctionalInterface {

    public static void doSomething(MyFunctionalInterface inner){
        inner.myMethod();
    }
    public static void main(String[] args) {
        doSomething(() -> System.out.println("Lambada表達式執行了"));
    }
}
           

1.4自定義函數式接口(有參有傳回)

定義一個函數式接口 Sumable ,内含抽象 sum 方法,可以将兩個int數字相加傳回int結果。使用該接口作為方法 的參數,并進而通過Lambda來使用它。

import java.lang.FunctionalInterface;

@FunctionalInterface
public interface Sumable {
    int sum(int a,int b);
}

public class DemoSumable {

    public static void showSum(int x, int y, Sumable sumable) {
        System.out.println(sumable.sum(x, y));
    }
    public static void main(String[] args) {
        showSum(10,20,(m,n) -> m+n);
    }
}

           

1.5 Lambda的延遲執行

一種典型的場景就是對參數進行有條件使用,例如對日志消息進行拼接後,在滿足條件的情況下進行列印輸出:

public class Demo01Logger {
    private static void log(int level, String msg) {
        if (level == 1) {
            System.out.println(msg);
        }
    }
    public static void main(String[] args) {
        String msgA = "Hello";
        String msgB = "World";
        String msgC = "Java";
        log(1, msgA + msgB + msgC);
    }
}
           

這段代碼存在問題:無論級别是否滿足要求,作為 log 方法的第二個參數,三個字元串一定會首先被拼接并傳入方 法内,然後才會進行級别判斷。如果級别不符合要求,那麼字元串的拼接操作就白做了,存在性能浪費。

剛剛我們體驗了,日志案例中的性能浪費,現在我們該用函數式程式設計,即使用Lambda!! 我們把日志拼接功能交給函數式接口,調用接口中的方法來拼接,而不是我們自己拼接

使用Lambda必然需要一個函數式接口:

@FunctionalInterface 
public interface MessageBuilder {
  String buildMessage();
}

public class Demo02Logger {
  private static void log(int level, MessageBuilder builder) { 
   if (level == 1) { 
    System.out.println(builder.buildMessage()); 
   }     
 } 
 public static void main(String[] args) {
       String msgA = "Hello";
       String msgB = "World";
       String msgC = "Java";
       log(1, () ‐> msgA + msgB + msgC );
 }     
}
           

這樣一來,隻有當級别滿足要求的時候,才會進行三個字元串的拼接;否則三個字元串将不會進行拼接。

下面的代碼可以通過結果進行驗證

public class Demo02Logger {
    private static void log(int level, MessageBuilder builder) {
        if (level == 1) {
            System.out.println(builder.buildMessage());
        }
    }
    public static void main(String[] args) {
        String msgA = "Hello";
        String msgB = "World";
        String msgC = "Java";
        log(2, () -> {
            System.out.println("Lambada表達式執行了");
            return msgA + msgB + msgC;
        });
    }
}
           

從結果中可以看出,在不符合級别要求的情況下,Lambda将不會執行。進而達到節省性能的效果。

1.6 使用Lambda作為參數和傳回值

如果抛開實作原理不說,Java中的Lambda表達式可以被當作是匿名内部類的替代品。如果方法的參數是一個函數 式接口類型,那麼就可以使用Lambda表達式進行替代。使用Lambda表達式作為方法參數,其實就是使用函數式 接口作為方法參數。

例如 java.lang.Runnable 接口就是一個函數式接口,假設有一個 startThread 方法使用該接口作為參數,那麼就 可以使用Lambda進行傳參。這種情況其實和 Thread 類的構造方法參數為 Runnable 沒有本質差別。

public class Demo04Runnable {
    public static void startThread(Runnable task){
        new Thread(task).start();
    }
    public static void main(String[] args) {
        startThread(() -> System.out.println("線程任務執行"));
    }
}
           

類似地,如果一個方法的傳回值類型是一個函數式接口,那麼就可以直接傳回一個Lambda表達式。當需要通過一 個方法來擷取一個 java.util.Comparator 接口類型的對象作為排序器時:

import java.util.Arrays;
import java.util.Comparator;
    public class Demo06Comparator {
        private static Comparator<String> getComparator() {
            return (a, b) -> b.length() - a.length();
        }
        public static void main(String[] args) {
            String[] array = {"a", "ab", "a"};
            Thread thread = new Thread();
            System.out.println(Arrays.toString(array));
            Arrays.sort(array, getComparator());
            System.out.println(Arrays.toString(array));
        }
    }
           

1.7自定義Lambda參數和傳回值

自定義一個函數式接口 MySupplier ,含有無參數的抽象方法 get 得到 Object 類型的傳回值。并使用該函數式 接口分别作為方法的參數和傳回值

使用該接口作為方法的參數,并且在傳遞參數時将實際參數寫成Lambda:

@FunctionalInterface
 public interface MySupplier {
       Object get();
 }
 public class Demo1MySupplier {
    private static void printParam(MySupplier supplier) { 
       Object obj = supplier.get();
       System.out.println(obj);
  } 
    public static void main(String[] args) { 
           printParam(() ‐> "Hello");
  }
 }
           

使用該接口作為方法的傳回值,也很簡單:

public class Demo2MySupplier {
    private static MySupplier getData() {
            return () ‐> "Hello";
    }
    public static void main(String[] args) {
            MySupplier ms = getData();
            Object obj = ms.get(); 
            System.out.println(obj);
    }
}
           

1.8方法的引用

備援的Lambda場景

@FunctionalInterface
 public interface Printable {
    void print(String str);
 }
           

在 Printable 接口當中唯一的抽象方法 print 接收一個字元串參數,目的就是為了列印顯示它。那麼通過Lambda 來使用它的代碼很簡單:

public class PrintSimple {
    private static void printString(Printable data) {
         data.print("Hello, World!");
    }
    public static void main(String[] args) { 
         printString(s ‐> System.out.println(s));
    }
}
           

其中 printString 方法隻管調用 Printable 接口的 print 方法,而并不管 print 方法的具體實作邏輯會将字元串 列印到什麼地方去。而 main 方法通過Lambda表達式指定了函數式接口 Printable 的具體操作方案為:拿到 String(類型可推導,是以可省略)資料後,在控制台中輸出它。

這段代碼的問題在于,對字元串進行控制台列印輸出的操作方案,明明已經有了現成的實作,那就是 System.out 對象中的 println(String) 方法。既然Lambda希望做的事情就是調用 println(String) 方法,那何必自己手動調 用呢?

1.9 用方法引用改進代碼

能否省去Lambda的文法格式(盡管它已經相當簡潔)呢?隻要“引用”過去就好了:

public class PrintRef {
    private static void printString(Printable data) {
        data.print("Hello, World!"); 
    }
     public static void main(String[] args) {
        printString(System.out::println);
     } 
}
           

請注意其中的雙冒号 :: 寫法,這被稱為“方法引用”,而雙冒号是一種新的文法。

2.0方法引用符

雙冒号 :: 為引用運算符,而它所在的表達式被稱為方法引用。

如果Lambda要表達的函數方案已經存在于某個方法的實作中,那麼則可以通過雙冒号來引用該方法作為Lambda 的替代者。

語義分析

例如上例中, System.out 對象中有一個重載的 println(String) 方法恰好就是我們所需要的。那麼對于 printString 方法的函數式接口參數,對比下面兩種寫法,完全等效:

Lambda表達式寫法:

s -> System.out.println(s);

方法引用寫法: System.out::println

第一種語義是指:拿到參數之後經Lambda之手,繼而傳遞給 System.out.println 方法去處理。

第二種等效寫法的語義是指:直接讓 System.out 中的 println 方法來取代Lambda。兩種寫法的執行效果完全一 樣,而第二種方法引用的寫法複用了已有方案,更加簡潔。

推導與省略

如果使用Lambda,那麼根據“可推導就是可省略”的原則,無需指定參數類型,也無需指定的重載形式——它們都 将被自動推導。而如果使用方法引用,也是同樣可以根據上下文進行推導。

函數式接口是Lambda的基礎,而方法引用是Lambda的孿生兄弟。 下面這段代碼将會調用 println 方法的不同重載形式,将函數式接口改為int類型的參數:

@FunctionalInterface
public interface PrintableInteger {
    void print(int str);
}
           

由于上下文變了之後可以自動推導出唯一對應的比對重載,是以方法引用沒有任何變化:

public class PrintOverload {
    private static void printInteger(PrintableInteger data) {
      data.print(1024); 
     } 
    public static void main(String[] args) {
       printInteger(System.out::println);
    }
}
           

這次方法引用将會自動比對到 println(int) 的重載形式。

2.1 通過對象名引用成員方法

這是常見的一種用法,與上例相同。如果一個類中已經存在了一個成員方法:

public class MethodRefObject {
    public void printUpperCase(String str) {
       String bigStr = str.toUpperCase();
       System.out.println(bigStr);
     }
}
           

函數式接口仍然定義為:

@FunctionalInterface
public interface Printable {
    void print(String str);
}
           

那麼當需要使用這個 printUpperCase 成員方法來替代 Printable 接口的Lambda的時候,已經具有了 MethodRefObject 類的對象執行個體,則可以通過對象名引用成員方法,代碼為:

public class Demo04MethodRef {
     private static void printString(Printable lambda) {
        lambda.print("Hello");
     }
     public static void main(String[] args) {
        MethodRefObject obj = new MethodRefObject();
        printString(obj::printUpperCase);
      }
}
           

2.2 通過類名稱引用靜态方法

由于在 java.lang.Math 類中已經存在了靜态方法 abs ,是以當我們需要通過Lambda來調用該方法時,有兩種寫 法。首先是函數式接口:

@FunctionalInterface
public interface Calcable {
    int calc(int num);
 }
           

第一種寫法是使用Lambda表達式:

public class Demo05Lambda {
     private static void method(int num, Calcable lambda) {
        int result = lambda.calc(num);   
        System.out.println(result);
     } 
     public static void main(String[] args) {
             method(‐10, n ‐> Math.abs(n));
     }
}
           

但是使用方法引用的更好寫法是:

public class Demo06MethodRef {
     private static void method(int num, Calcable lambda) {
         System.out.println(lambda.calc(num));
     }       
     public static void main(String[] args) {
             method(‐10, Math::abs);
     }
}
           

在這個例子中,下面兩種寫法是等效的:

Lambda表達式: n -> Math.abs(n) 方法引用: Math::abs

假設有一個 StringUtils 字元串工具類,其中含有靜态方法 isBlank 如下:

public final class StringUtils {
    public static boolean isBlank(String str) {  
         return str == null || "".equals(str.trim());
    }
}
           

定義一個函數式接口 StringChecker ,其中的抽象方法 checkBlank 的預期行為與 isBlank 一緻,并定義一個 方法使用該函數式接口作為參數。通過方法引用的形式,将 StringUtils 工具類中的 isBlank 方法作為Lambda的 實作。

@FunctionalInterface
 public interface StringChecker {
     boolean checkString(String str);
  }
public class DemoStringChecker {
     private static void methodCheck(StringChecker checker) {
        System.out.println(checker.checkString("   "));
     }
     public static void main(String[] args) {
        methodCheck(StringUtils::isBlank);
      }
 }
           

2.3 通過super引用成員方法

如果存在繼承關系,當Lambda中需要出現super調用時,也可以使用方法引用進行替代。首先是函數式接口:

@FunctionalInterface
public interface Greetable {
    void greet();
}

public class Human {
    public void sayHello(){
        System.out.println("Hello!");
    }
}

public class Man extends Human {
    public void sayHello() {
         method(() -> super.sayHello());
    }
    private void method(Greetable lambda) {
        lambda.greet();
        System.out.println("I'm a man!");
    }
}
           

在這個例子中,下面兩種寫法是等效的:

Lambda表達式: () -> super.sayHello()

方法引用: super::sayHello

2.4通過this引用成員方法

this代表目前對象,如果需要引用的方法就是目前類中的成員方法,那麼可以使用“this::成員方法”的格式來使用方 法引用。首先是簡單的函數式接口:

@FunctionalInterface
 public interface Richable { 
     void buy();
 }
           

下面是一個丈夫 Husband 類:

public class Husband { 
    private void marry(Richable lambda) {
            lambda.buy();
    }
    public void beHappy() {
            marry(() ‐> System.out.println("買套房子"));
    }
}
           

方法 beHappy 調用了結婚方法 marry ,後者的參數為函數式接口 Richable ,是以需要一個Lambda表達式。 但是如果這個Lambda表達式的内容已經在本類當中存在了,則可以對 Husband 丈夫類進行修改:

public class Husband {
     private void buyHouse() {
             System.out.println("買套房子");
      } 
     private void marry(Richable lambda) {
                     lambda.buy(); 
      }
     public void beHappy() {
             marry(() ‐> this.buyHouse());
     }
}
           

如果希望取消掉Lambda表達式,用方法引用進行替換,則更好的寫法為:

public class Husband { 
    private void buyHouse() {
            System.out.println("買套房子");
    }
    private void marry(Richable lambda) {
            lambda.buy();
     } 
    public void beHappy() {
            marry(this::buyHouse);
    }
 }
           

在這個例子中,下面兩種寫法是等效的:

Lambda表達式: () -> this.buyHouse()

方法引用: this::buyHouse

2.5 類的構造器引用

由于構造器的名稱與類名完全一樣,并不固定。是以構造器引用使用 類名稱::new 的格式表示。首先是一個簡單 的 Person 類:

public class Person {
     private String name;
     public Person(String name) {
        this.name = name;
     } 
     public String getName() {
         return name; 
     }
}
           

然後是用來建立 Person 對象的函數式接口:

public interface PersonBuilder {
     Person buildPerson(String name);
}
           

要使用這個函數式接口,可以通過Lambda表達式:

public class Demo09Lambda {
  public static void printName(String name, PersonBuilder builder) {
      System.out.println(builder.buildPerson(name).getName());
  }
  public static void main(String[] args) {
          printName("趙麗穎", name ‐> new Person(name)); 
   }
}
           

2.6 數組的構造器引用

數組也是 Object 的子類對象,是以同樣具有構造器,隻是文法稍有不同。如果對應到Lambda的使用場景中時, 需要一個函數式接口:

@FunctionalInterface 
public interface ArrayBuilder {
    int[] buildArray(int length);
 }
           

在應用該接口的時候,可以通過Lambda表達式:

public class Demo11ArrayInitRef {
        private static int[] initArray(int length, ArrayBuilder builder) {
        return builder.buildArray(length); 
        }
        public static void main(String[] args) { 
        int[] array = initArray(10, length ‐> new int[length]);
         }
 }
           

但是更好的寫法是使用數組的構造器引用:

public class Demo12ArrayInitRef {
     private static int[] initArray(int length, ArrayBuilder builder) {
             return builder.buildArray(length);
     }
     public static void main(String[] args) { 
             int[] array = initArray(10, int[]::new);
     }
}
           

在這個例子中,下面兩種寫法是等效的:

Lambda表達式: length -> new int[length]

方法引用: int[]::new

備注:數組的構造器引用,可以和Java 8的Stream API結合,在一定程度上“解決”集合中 toArray 方法的泛型擦除問題。