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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > windows >内容正文

windows

系统学习Lambda表达式

發布時間:2025/3/21 windows 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 系统学习Lambda表达式 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1. 語法

首先我們要知道如何寫Lambda表達式,或者說怎么樣才能寫出有效的Lambda表達式,這就需要了解其語法。

Lambda表達式由三部分組成:

  • 參數列表

  • 箭頭

  • 主體

  • 兩種風格,分別是:

  • 表達式-風格

    (parameters) -> expression

  • 塊-風格

    (parameters) -> { statements; }

  • 依據上面的風格介紹,來試著判斷下面給出的示例是否有效:

    () -> {}() -> "Apple"() -> { return "Apple"; }(Integer i) -> return "Apple" + i(String s) -> { "Apple"; }

    解析:(1)是塊風格,沒有語句;(2)是表達式風格,一個字符串表達式;(3)是塊風格,有花括號和返回語句;(4)非有效,寫了返回語句,但缺少花括號,補上花括號和分號,為塊風格,而去掉return則為表達式風格;(5)非有效,"Apple"是一個字符串表達式,不是一個語句,加上return,或者去掉分號和花括號。

    ?

    2. 函數式接口

    Lambda表達式寫好了,我們要知道哪里能用Lambda表達式。已知Lambda表達式可看作是匿名內部類的實現,那對于匿名內部類來說最重要的是類所實現的接口,而Lambda表達式是否可用于所有接口?答案“不是的”,Lambda表達式對接口有一定的要求,必須是函數式接口

    所謂的函數式接口指的是只定義一個抽象方法的接口

    例如:

    public?interface?Comparator<T>?{int?compare(T?o1,?T?o2); }public?interface?Runnable?{void?run(); }public?interface?Callable<V>?{V?call()?throws?Exception; }

    可見上面三個接口都只有一個抽象方法,但是三個方法的簽名都不一樣,這要求Lambda表達式與實現接口的方法簽名要一致。下面用函數描述符來表示上述三個方法的簽名,箭頭前面是方法的入參類型,后面是返回類型。

  • compare:(T, T) -> int,兩個泛型T類型的入參,返回int類型

    Lambda表達式:(Apple a1, Apple a2) -> a1.getWeight - a2.getWeight

  • run:() -> void,無入參,無返回值

    Lambda表達式:() -> { System.out.println("Hi"); }

  • call:() -> V,無入參,返回一個泛型V類型的對象

    Lambda表達式:() -> new Apple()

  • 看call方法的示例,你是否會疑惑,new Apple()是一個語句,為什么沒有花括號和分號,是不是非有效的。你需要記住這是合法的,這是一個特殊的規定,不需要用括號環繞返回值為void的單行方法調用

    ?

    3. 常用的函數式接口

    下面介紹在Java中內置的常用Lambda表達式:

    3.1 Predicate

    public?interface?Predicate<T>?{boolean?test(T?t); }

    test:T -> boolean,接收一個泛型T對象,返回一個boolean。

    適用場景:表示一個涉及類型T的布爾表達式。

    //?判斷空白字符串 Predicate<String>?blankStrPredicate?=?s?->?s?!=?null?&&?s.trim().length()?==?0; blankStrPredicate.test("??");?//?true //?判斷蘋果重量是否大于150 Predicate<Apple>?heavyApplePredicate?=?a?->?a.getWeight()?>?150; heavyApplePredicate.test(new?Apple(100));?//?false

    注意,參數部分缺少了參數類型,是因為可根據上下文推斷出Lambda表達式的參數類型,所以可以省略不寫。比如這里因為將Lambda表達式賦值給一個Predicate類型的變量,又因為函數描述符為(T) -> boolean,則可推斷出參數T的實際類型為String。而且當只有一個參數時,可以將括號也省略。

    3.2 Consumer

    public?interface?Consumer<T>?{void?accept(T?t); }

    accept:T -> void,接收一個泛型T對象,無返回值(void)。

    適用場景:訪問類型T的對象,對其執行某些操作。

    //?打印蘋果重量 Consumer<Apple>?appleWeighter?=?a?->?System.out.println("The?apple?weights?"?+?a.getWeight()?+?"?grams"); appleWeighter.accept(new?Apple(200));? //?The?apple?weights?200?grams

    3.3 Supplier

    public?interface?Supplier<T>?{T?get(); }

    get:() -> T,無入參,返回一個泛型T對象。

    適用場景:定義類型T的對象的生產規則。

    Consumer<Apple>?appleWeighter?=(a)?->?System.out.println("The?apple?weights?"?+?a.getWeight()?+?"?grams"); //?生產200克的蘋果 Supplier<Apple>?heavyAppleSupplier?=?()?->?new?Apple(200); appleWeighter.accept(heavyAppleSupplier.get());

    3.4 Function

    public?interface?Function<T,?R>?{R?apply(T?t); }

    apply:T -> R,接受一個泛型T對象,返回一個泛型R對象

    適用場景:將輸入對象轉換輸出。

    double?unitPrice?=?0.01; //?計算蘋果價格 Function<Apple,?Double>?priceAppleFunction?=?a?->?a.getWeight()?*?unitPrice; priceAppleFunction.apply(new?Apple(100));?//?1

    這里做個補充,上面這段代碼特別的地方在于使用到了外部的局部變量。Lambda表達式使用外部變量有什么要求?對于Lambda表達式所在的主體(類)的實例變量和靜態變量,可以無限制使用,但局部變量必須顯示聲明為final或實際上是final的。

    聲明為final好理解,什么是實際上是final的,意思就是不能被代碼進行修改,比如這里的unitPrice雖然沒有聲明為final,但后續的代碼并沒有修改該變量,所以實際上也是final的。感興趣的讀者可以自己試下,對unitPrice進行修改,看下會發生什么。

    3.5 Comparator

    public?interface?Comparator<T>?{int?compare(T?o1,?T?o2); }

    compare:(T, T) -> int,兩個泛型T類型的入參,返回int類型

    適用場景:比較兩個對象

    Comparator<Apple>?weightComparator?=?(a1,?a2)?->?a1.getWeight()?-?a2.getWeight(); weightComparator.compare(new?Apple(100),?new?Apple(150));?//?-1

    ?

    4. 方法引用

    Java還提供了一種更簡潔的寫法,先上示例:

    Function<Apple,?Integer>?weightor?=?a?->?a.getWeight();

    可改寫為:

    Function<Apple,?Integer>?weightor?=?Apple::getWeight;

    這種寫法被稱作方法引用,僅當在Lambda表達式中直接調用了一個方法時可以使用。其寫法為目標引用::方法名稱。

    根據目標引用可分為三類:

    (1)指向靜態方法的方法引用

    目標引用為,調用其靜態方法,例如:

    Function<String,?Integer>?fun?=?s?->?Integer.parseInt(s);

    調用了?Integer?的靜態方法?parseInt,可寫為:

    Function<String,?Integer>?fun?=?Integer::parseInt;

    (2)指向任意類型實例方法的方法引用

    目標引用為實例對象,調用其實例方法,例如:

    Function<String,?Integer>?fun?=?s?->?s.length();

    調用了?String?類型實例?s?的?length?方法,可寫為:

    Function<String,?Integer>?fun?=?String::length;

    目標引用寫實例的類型。和第一種寫法相同,只不過第一種的方法是靜態方法,這里是實例方法。

    (3)指向現存外部對象實例方法的方法引用

    目標引用為現存外部對象,調用其實例方法,例如:

    String?s?=?"草捏子"; Supplier<Integer>?len?=?()?->?s.length();

    調用了局部變量?s?的?length?方法,可寫為:

    String?s?=?"草捏子"; Supplier<Integer>?len?=?s::length;

    目標引用寫變量名,區別于前兩種。

    ?

    5. 復合Lambda表達式

    之前的例子都是使用的單個Lambda表達式,現在我們把多個Lambda表達式組合在一起,構建更復雜一點的表達式。

    5.1 比較器復合(Comparator)

    我們使用?Comparator?對蘋果進行排序,按重量從小到大:

    List<Apple>?apples?=?Arrays.asList(new?Apple("red",?50),?new?Apple("red",?100),?new?Apple("green",?100)); apples.sort(Comparator.comparing(Apple::getWeight));

    對?Comparator?的靜態方法comparing?簡單介紹下,接受一個?Function?類型的參數,返回一個?Comparator?類型的實例,定義如下:

    public?static?<T,?U?extends?Comparable<??super?U>>?Comparator<T>?comparing(Function<??super?T,???extends?U>?keyExtractor) {Objects.requireNonNull(keyExtractor);return?(Comparator<T>?&?Serializable)(c1,?c2)?->?keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2)); }

    通過使用compareTo,實現了重量從小到大的排序,那想按重量從大到小排序,怎么辦呢?可以使用?Comparator?的?reversed?方法

    apples.sort(Comparator.comparing(Apple::getWeight).reversed());

    reversed?的實現如下:

    default?Comparator<T>?reversed()?{return?Collections.reverseOrder(this); }

    使用工具類得到一個反序的?Comparator。你可能會好奇Comparator?作為一個接口,reversed?方法可以有具體的實現,接口的實例方法應該都是抽象方法,那它還是一個有效的函數式接口嗎,或者說還是一個有效的接口嗎?

    回想下第二節的內容,函數式接口是只定義一個抽象方法的接口。Comparator的抽象方法只有一個?compare,其他是具體方法,所以是合法的函數式接口。那么接口中為什么能定義具體方法呢?Java8 之前是不支持的,但在 Java8 中引入了?default?關鍵字

    當在接口中用default聲明一個方法時,允許它是一個具體方法。這樣的好處在于,我們可以在Lambda表達式之后直接跟上一個具體方法,對Lambda表達式增強,實現更復雜的功能。在后文介紹的用于復合表達式的方法都是接口中的?default?方法。

    下面我們試著實現更復雜的排序,在按重量從大到小排序后,按顏色排序:

    apples.sort(Comparator.comparing(Apple::getWeight).reversed()); apples.sort(Comparator.comparing(Apple::getColor));

    先后用兩個Comparator。而使用?Comparator?的?thenComparing?方法可以繼續連接一個?Comparator,從而構建更復雜的排序:

    apples.sort(Comparator.comparing(Apple::getWeight).reversed().thenComparing(Apple::getColor));

    5.2 謂詞復合(Predicate)

    Predicate?的?test?方法?(T) -> boolean返回一個布爾表達式。類似 Java 在為布爾表達式提供的與或非,Predicate中也有對應的方法?andornegate。例如:

    //?重的蘋果 Predicate<Apple>?heavyApple?=?a?->?a.getWeight()?>?100; //?紅的蘋果 Predicate<Apple>?redApple?=?a?->?a.getColor().equals("red");//?輕的蘋果 Predicate<Apple>?lightApple?=?heavyApple.negate(); //?不紅的蘋果 Predicate<Apple>?nonRedApple?=?redApple.negate(); //?重且紅的蘋果 Predicate<Apple>?heavyAndRedApple?=?heavyApple.and(redApple); //?重或紅的蘋果 Predicate<Apple>?heavyOrRedApple?=?heavyApple.or(redApple);

    5.3 函數復合(Function)

    Function?(T) -> R,對輸入做映射。我們通過將多個Function進行組合,實現將一個Function的輸出作為另一個Function的輸入,是不是有管道的感覺。下面請看具體的方法。

    andThen方法,a.andThen(b),將先執行a,再執行b。

    Function<Integer,?Integer>?f?=?x?->?x?+?1; Function<Integer,?Integer>?g?=?x?->?x?*?2; Function<Integer,?Integer>?h?=?f.andThen(g); int?result?=?h.apply(1);?//?4

    compose方法,a.compose(b),將先執行b,再執行a。

    Function<Integer,?Integer>?f?=?x?->?x?+?1; Function<Integer,?Integer>?g?=?x?->?x?*?2; Function<Integer,?Integer>?h?=?f.compose(g); int?result?=?h.apply(1);?//?3

    ?

    總結

    以上是生活随笔為你收集整理的系统学习Lambda表达式的全部內容,希望文章能夠幫你解決所遇到的問題。

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