日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java8新特性之lambda表达式(及方法引用与构造器引用)

發布時間:2025/3/15 编程问答 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java8新特性之lambda表达式(及方法引用与构造器引用) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Lambda 允許把函數作為一個方法的參數(函數作為參數傳遞進方法中)。使用 Lambda 表達式可以使代碼變的更加簡潔緊湊。

一、語法

lambda 表達式的語法格式如下:

(parameters) -> expression 或 (parameters) ->{ statements; }

以下是lambda表達式的重要特征:

  • 可選類型聲明:不需要聲明參數類型,編譯器可以統一識別參數值。 可選的參數圓括號:一個參數無需定義圓括號,但多個參數需要定義圓括號。
  • 可選的大括號:如果主體包含了一個語句,就不需要使用大括號。
  • 可選的返回關鍵字:如果主體只有一個表達式返回值則編譯器會自動返回值,大括號需要指定明表達式返回了一個數值。
  • 二、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)

    在 Java8Tester.java 文件輸入以下代碼:

    Java8Tester.java 文件

    public class Java8Tester {public static void main(String args[]){Java8Tester tester = new Java8Tester();// 類型聲明MathOperation addition = (int a, int b) -> a + b;// 不用類型聲明MathOperation subtraction = (a, b) -> a - b;// 大括號中的返回語句MathOperation multiplication = (int a, int b) -> { return a * b; };// 沒有大括號及返回語句MathOperation division = (int a, int b) -> a / b;System.out.println("10 + 5 = " + tester.operate(10, 5, addition));System.out.println("10 - 5 = " + tester.operate(10, 5, subtraction));System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication));System.out.println("10 / 5 = " + tester.operate(10, 5, division));// 不用括號GreetingService greetService1 = message ->System.out.println("Hello " + message);// 用括號GreetingService greetService2 = (message) ->System.out.println("Hello " + message);greetService1.sayMessage("Runoob");greetService2.sayMessage("Google");}interface MathOperation {int operation(int a, int b);}interface GreetingService {void sayMessage(String message);}private int operate(int a, int b, MathOperation mathOperation){return mathOperation.operation(a, b);} }

    執行以上腳本,輸出結果為:

    $ javac Java8Tester.java $ java Java8Tester 10 + 5 = 15 10 - 5 = 5 10 x 5 = 50 10 / 5 = 2 Hello Runoob Hello Google

    三、使用 Lambda 表達式需要注意以下兩點

  • Lambda 表達式主要用來定義行內執行的方法類型接口,例如,一個簡單方法接口。在上面例子中,我們使用各種類型的Lambda表達式來定義MathOperation接口的方法。然后我們定義了sayMessage的執行。Lambda 表達式免去了使用匿名方法的麻煩,并且給予Java簡單但是強大的函數化的編程能力。
    變量作用域
  • lambda 表達式只能引用標記了 final 的外層局部變量,這就是說不能在 lambda 內部修改定義在域外的局部變量,否則會編譯錯誤。
    在 Java8Tester.java 文件輸入以下代碼:
  • Java8Tester.java 文件

    public class Java8Tester {final static String salutation = "Hello! ";public static void main(String args[]){GreetingService greetService1 = message -> System.out.println(salutation + message);greetService1.sayMessage("Runoob");}interface GreetingService {void sayMessage(String message);} }

    執行以上腳本,輸出結果為:

    $ javac Java8Tester.java $ java Java8Tester Hello! Runoob

    我們也可以直接在 lambda 表達式中訪問外層的局部變量:

    Java8Tester.java 文件

    public class Java8Tester {public static void main(String args[]) {final int num = 1;Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num));s.convert(2); // 輸出結果為 3}public interface Converter<T1, T2> {void convert(int i);} }

    lambda 表達式的局部變量可以不用聲明為 final,但是必須不可被后面的代碼修改(即隱性的具有 final 的語義)

    int num = 1; Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num)); s.convert(2); num = 5; //報錯信息:Local variable num defined in an enclosing scope must be final or effectively final 在 Lambda 表達式當中不允許聲明一個與局部變量同名的參數或者局部變量。String first = ""; Comparator<String> comparator = (first, second) -> Integer.compare(first.length(), second.length()); //編譯會出錯

    四、lambda表達式的常見使用場景

  • 列表迭代
  • 對一個列表的每一個元素進行操作,不使用 Lambda 表達式時如下:

    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); for (int element : numbers) {System.out.prinln(element); }

    使用 Lambda 表達式:

    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); numbers.forEach(x -> System.out.println(x));

    如果只需要調用單個函數對列表元素進行處理,那么可以使用更加簡潔的 方法引用 代替 Lambda 表達式:

    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); numbers.forEach(System.out::println);
  • 事件監聽
  • 不使用 Lambda 表達式:

    button.addActionListener(new ActionListener(){@Overridepublic void actionPerformed(ActionEvent e) {//handle the event} });

    使用 Lambda 表達式,需要編寫多條語句時用花括號包圍起來:

    button.addActionListener(e -> {//handle the event });
  • Predicate 接口
  • java.util.function 包中的 Predicate 接口可以很方便地用于過濾。如果你需要對多個對象進行過濾并執行相同的處理邏輯,那么可以將這些相同的操作封裝到 filter 方法中,由調用者提供過濾條件,以便重復使用。

    不使用 Predicate 接口,對于每一個對象,都需要編寫過濾條件和處理邏輯:

    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); List<String> words = Arrays.asList("a", "ab", "abc");numbers.forEach(x -> {if (x % 2 == 0) {//process logic} }) words.forEach(x -> {if (x.length() > 1) {//process logic} })

    使用 Predicate 接口,將相同的處理邏輯封裝到 filter 方法中,重復調用:

    public static void main(String[] args) {List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);List<String> words = Arrays.asList("a", "ab", "abc");filter(numbers, x -> (int)x % 2 == 0);filter(words, x -> ((String)x).length() > 1); }public static void filter(List list, Predicate condition) {list.forEach(x -> {if (condition.test(x)) {//process logic}}) }

    filter 方法也可寫成:

    public static void filter(List list, Predicate condition) {list.stream().filter(x -> condition.test(x)).forEach(x -> {//process logic}) }
  • Map 映射
  • 使用 Stream 對象的 map 方法將原來的列表經由 Lambda 表達式映射為另一個列表,并通過 collect 方法轉換回 List 類型:

    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); List<Integer> mapped = numbers.stream().map(x -> x * 2).collect(Collectors.toList()); mapped.forEach(System.out::println);
  • Reduce 聚合
  • reduce 操作,就是通過二元運算對所有元素進行聚合,最終得到一個結果。例如使用加法對列表進行聚合,就是將列表中所有元素累加,得到總和。因此,我們可以為 reduce 提供一個接收兩個參數的 Lambda 表達式,該表達式就相當于一個二元運算:

    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); int sum = numbers.stream().reduce((x, y) -> x + y).get(); System.out.println(sum);
  • 代替 Runnable
  • 以創建線程為例,使用 Runnable 類的代碼如下:

    Runnable r = new Runnable() {@Overridepublic void run() {//to do something} }; Thread t = new Thread(r); t.start();

    使用 Lambda 表達式:

    Runnable r = () -> {//to do something }; Thread t = new Thread(r); t.start();

    或者使用更加緊湊的形式:

    Thread t = new Thread(() -> {//to do something }); t.start;

    五、方法引用

  • 什么是方法引用?
  • 方法引用是對Lambda表達式符合某種情況下的一種縮寫,使得我們的Lambda表達式更加的精簡, 也可以理解為Lambda表達式的另一種表現形式(縮寫)

  • 什么時候使用方法引用?
  • 當要傳遞給Lambda體內的操作,已經有實現的方法了,就可以使用方法引用了

  • 方法引用使用的前提條件是什么?
  • 1.方法引用所引用的方法的參數列表必須要和函數式接口中抽象方法的參數列表相同(完全一致)
    2.方法引用所引用的方法的的返回值必須要和函數式接口中抽象方法的返回值相同(完全一致)

  • 方法引用有什么語法格式嗎?
  • 方法引用一般有三種格式:
    1. 實例對象名::實例方法名
    2. 類名::靜態方法名
    3. 類名::實例方法名 (注意區別2,3的區別,下面會說)
    2,3的區別:
    若Lambda 的參數列表的第一個參數,是實例方法的調用者,第二個參數(或無參)是實例方法的參數時,格式: 類名::實例方法名

  • 方法引用舉例:
    • 舉例一:1. 實例對象名::實例方法名(案例一)
    Student student = new Student("XiangYang",23);Supplier<String> supplier = ()->student.getName();System.out.println("Lambda形式: "+supplier.get());Supplier<String> supplier1 = student :: getName;System.out.println("方法引用形式: "+supplier1.get());class Student{private String name;private int age;public Student(String name, int age) {this.name = name;this.age = age;}public Student(String name) {this.name = name;}public Student(int age) {this.age = age;}public Student() {}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;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +'}';} }
    • 舉例一: 1. 實例對象名::實例方法名 (案例二)
    @org.junit.Testpublic void test6(){//傳統Lambda表達式Consumer<String> consumer = (x) -> System.out.println(x);consumer.accept("Hi: 我是Lambda表達式實現的!"); //打印:Hi: 我是Lambda表達式實現的!//方法引用實現consumer = System.out::println;consumer.accept("Hello : XiangYang,我是使用方法引用實現的 ");//打印:Hello : XiangYang,我是使用方法引用實現的 }
    • 舉例二: 2. 類名::靜態方法名
    public class Test{@org.junit.Testpublic void test8(){//傳統Lambda表達式Consumer<String> consumer = (str) -> sop(str);consumer.accept("Hello : XiangYang"); //打印:Hello : XiangYang//方法引用方式consumer = Test::sop;consumer.accept("Hello : XiangYang"); //打印:Hello : XiangYang} }
    • 舉例三: 3. 類名::實例方法名
    @org.junit.Testpublic void test7(){//傳統Lambda表達式BiPredicate<String,String> biPredicate = (x,y) -> x.equals(y);boolean test = biPredicate.test("hello", "hi");System.out.println(test);//false//方法引用biPredicate = String::equals;test = biPredicate.test("hello", "hello");System.out.println(test);//true}
  • 注意:
  • 1.這里一定要知道 類名::實例方法名 這種語法的使用條件:說明:a,b是所有方法引用的必要條件,c是使用 類名::實例方法名的特殊前提a.方法引用所引用的方法的參數列表必須要和函數式接口中抽象方法的參數列表相同(完全一致)b.方法引用所引用的方法的的返回值必須要和函數式接口中抽象方法的返回值相同(完全一致)c.若Lambda 的參數列表的第一個參數,是實例方法的調用者,第二個參數(或無參)是實例方法的參數時,格式: 類名::實例方法名 2.解釋一下:首先看一下使用傳統Lambda表達式的形式://傳統Lambda表達式BiPredicate<String,String> biPredicate = (x,y) -> x.equals(y);boolean test = biPredicate.test("hello", "hi");System.out.println(test);//false注意看: equals方法的調用者是x,equals方法參數是y,這符合我們上面的c條件;所以能使用
  • 方法引用總結:
  • 1.一定弄明白使用方法引用的前提條件(2個)a. 方法引用所引用的方法的參數列表必須要和函數式接口中抽象方法的參數列表相同(完全一致)b .方法引用所引用的方法的的返回值必須要和函數式接口中抽象方法的返回值相同(完全一致)這里使用代碼解釋一下:@org.junit.Testpublic void test6(){//傳統Lambda表達式Consumer<String> consumer = (x) -> System.out.println(x);consumer.accept("Hi: 我是Lambda表達式實現的!"); //打印:Hi: 我是Lambda表達式實現的!//方法引用實現consumer = System.out::println;consumer.accept("Hello : XiangYang,我是使用方法引用實現的 ");//打印:Hello : XiangYang,我是使用方法引用實現的 }解釋:1.首先我們看一下Consumer的函數式接口的源碼的接口void accept(T t);由于我們給泛型傳入的是String類型Consumer<String> consumer = (x) -> System.out.println(x);所以此時//這是函數式接口中抽象方法,接收了實際類型String,沒有返回值void accept(T t); ==void accept(String t); 2 我們在看我們的Lambda體內的實現 System.out.println(x);此時,我們查看println方法的源碼://這是 方法引用所引用的方法 的參數類型是String,無返回值public void println(String x) {synchronized (this) {print(x);newLine();}}3. 此時函數式的接口中的參數類型與返回值 和 方法引用所引用方法的參數類型與返回值相同,所以可以使用方法引用,類型為: 實例對象名:: 實例方法名

    六、構造器引用

  • 什么是構造器引用?
  • 與函數式接口相結合,自動與函數式接口中方法兼容。
    可以把構造器引用賦值給定義的方法。

  • 什么時候用構造器引用?
  • 在使用Lambda表達的時,滿足使用構造器引用條件的時候可以使用

  • 構造器使用的前提是什么?
  • 構造器參數列表要與接口中抽象方法的參數列表一致!

  • 構造器引用的語法格式是什么?
  • 語法格式:
    類名 :: new

  • 舉例:
  • 說明:Student類見方法引用舉例一的案例@org.junit.Testpublic void test9(){//傳統Lambda方式Supplier<Student> studentSupplier = ()-> new Student();Student student = studentSupplier.get();System.out.println(student);//Student{name='null', age=0}//構造器引用studentSupplier = Student::new;student = studentSupplier.get();System.out.println(student);//Student{name='null', age=0}}
  • 案例說明:
  • 1.首先我們要只是Supplier接口的抽象方法定義為:T get();我們代碼Supplier<Student>此時已經泛型T傳入Student類型相當于:Student get(); //函數式接口中是無參,返回值為Student 2.在看我們的Student類的定義中一個無參構成器Student(){} //構造器無參,返回Student類型(嚴格意義上兩個構成函數沒有返回值,我們先這樣理解)3.此時,符合我們使用構造方法引用的前提:構造器參數列表要與接口中抽象方法的參數列表一致!所以:可以使用構造引用。

    七、數組引用

    這里單獨把數組引用拿出來實際上是強調一下,數組引用和構造引用基本相同,直接上一個案例來說明問題

  • 案例:
  • @org.junit.Testpublic void test10(){//需求:獲取n個int[] 數組//傳統Lambda實現Function<Integer,int[]> function = (i) -> new int[i];int[] apply = function.apply(5);System.out.println(apply.length); // 5//數組類型引用實現function = int[] ::new;apply = function.apply(10);System.out.println(apply.length); // 10}

    文章轉自
    文章轉自
    文章轉自

    總結

    以上是生活随笔為你收集整理的java8新特性之lambda表达式(及方法引用与构造器引用)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。