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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

你真的了解 lambda 吗(纠错篇)?

發布時間:2025/3/21 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 你真的了解 lambda 吗(纠错篇)? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

來源:http://uee.me/aWWS2

1. 用法

示例:最普遍的一個例子,執行一個線程

new Thread(() -> System.out.print("hello world")).start();

->??我們發現它指向的是?Runnable接口

@FunctionalInterface public interface Runnable {/*** When an object implementing interface <code>Runnable</code> is used* to create a thread, starting the thread causes the object's* <code>run</code> method to be called in that separately executing* thread.* <p>* The general contract of the method <code>run</code> is that it may* take any action whatsoever.** @see ? ? java.lang.Thread#run()*/public abstract void run();}

分析

  • ->這個箭頭是lambda表達式的關鍵操作符

  • ->把表達式分成兩截,前面是函數參數,后面是函數體。

  • Thread的構造函數接收的是一個Runnable接口對象,而我們這里的用法相當于是把一個函數當做接口對象傳遞進去了,這點理解很關鍵,這正是函數式編程的含義所在。

  • 我們注意到Runnable有個注解?@FunctionalInterface,它是jdk8才引入,它的含義是函數接口。它是lambda表達式的協議注解,這個注解非常重要,后面做源碼分析會專門分析它的官方注釋,到時候一目了然。

  • /* @jls 4.3.2. The Class Object * @jls 9.8 Functional Interfaces * @jls 9.4.3 Interface Method Body * @since 1.8 */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface FunctionalInterface {}

    2. 由此引發的一些案例

    有參數有返回值的實例:集合排序

    List<String> list = new ArrayList<>(); Collections.sort(list, (o1, o2) -> {if(o1.equals(o2)) {return 1;}return -1; })

    我們知道Collections.sort方法的第二個參數接受的是一個?Comparator<T>的對象,它的部分關鍵源碼是這樣的:

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

    如上已經去掉注釋和部分其他方法。

    我們可以看到sort的第二個參數是Comparator的compare方法,參數類型是T,分別是o1和o2,返回值是一個int。

    3. 疑問

    • 上面的示例我們看到接口都有個?@FunctionalInterface的注解,但是我們在實際編程中并沒有加這個注解也可以實現lambda表達式,例如:

    public class Main {interface ITest {int test(String string);}static void Print(ITest test) {test.test("hello world");}public static void main(String[] args) {Print(string -> {System.out.println(string);return 0;});} }

    如上所示,確實不需要增加?@FunctionInterface注解就可以實現。

    • 如果在1中的示例的ITest接口中增加另外一個接口方法,我們會發現不能再用lambda表達式。

    我們帶著這兩個疑問來進入源碼解析。

    4. 源碼解析

    必須了解注解?@FunctionInterface

    上源碼:

    package java.lang;import java.lang.annotation.*;/** * An informative annotation type used to indicate that an interface * type declaration is intended to be a <i>functional interface</i> as * defined by the Java Language Specification. * * Conceptually, a functional interface has exactly one abstract * method. ?Since {@linkplain java.lang.reflect.Method#isDefault() * default methods} have an implementation, they are not abstract. ?If * an interface declares an abstract method overriding one of the * public methods of {@code java.lang.Object}, that also does * <em>not</em> count toward the interface's abstract method count * since any implementation of the interface will have an * implementation from {@code java.lang.Object} or elsewhere. * * <p>Note that instances of functional interfaces can be created with * lambda expressions, method references, or constructor references. * * <p>If a type is annotated with this annotation type, compilers are * required to generate an error message unless: * * <ul> * <li> The type is an interface type and not an annotation type, enum, or class. * <li> The annotated type satisfies the requirements of a functional interface. * </ul> * * <p>However, the compiler will treat any interface meeting the * definition of a functional interface as a functional interface * regardless of whether or not a {@code FunctionalInterface} * annotation is present on the interface declaration. * * @jls 4.3.2. The Class Object * @jls 9.8 Functional Interfaces * @jls 9.4.3 Interface Method Body * @since 1.8 */@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface FunctionalInterface {}

    我們說過這個注解用來規范lambda表達式的使用協議的,那么注釋中都說了哪些呢?

    • 一種給interface做注解的注解類型,被定義成java語言規范。

    * An informative annotation type used to indicate that an functional interface

    * type declaration is intended to be a <b>functional interface</b> as

    * defined by the Java Language Specification.

    • 一個被它注解的接口只能有一個抽象方法,有兩種例外。

    第一是接口允許有實現的方法。

    這種實現的方法是用default關鍵字來標記的

    (java反射中java.lang.reflect.Method#isDefault()方法用來判斷是否是default方法),例如:

    當然這是jdk8才引入的特性,到此我們才知道,知識是一直在變化的,我們在學校中學到interface接口不允許有實現的方法是錯誤的,隨著時間推移,一切規范都有可能發生變化。

    如果聲明的方法和java.lang.Object中的某個方法一樣,它可以不當做未實現的方法,不違背這個原則:一個被它注解的接口只能有一個抽象方法

    例如同樣是Compartor接口中,它重新聲明了equals方法:

    這些是對如下注釋的翻譯和解釋。

    * Conceptually, a functional interface has exactly one abstract * method. ?Since {@linkplain java.lang.reflect.Method#isDefault() * default methods} have an implementation, they are not abstract. ?If * an interface declares an abstract method overriding one of the * public methods of {@code java.lang.Object}, that also does * <em>not</em> count toward the interface's abstract method count * since any implementation of the interface will have an * implementation from {@code java.lang.Object} or elsewhere.
    • 如果一個類型被這個注解修飾,那么編譯器會要求這個類型必須滿足如下條件:

  • 這個類型必須是一個interface,而不是其他的注解類型、枚舉enum或者類class

  • 這個類型必須滿足function interface的所有要求,如你個包含兩個抽象方法的接口增加這個注解,會有編譯錯誤。

  • * <p>If a type is annotated with this annotation type, compilers are * required to generate an error message unless: * * <ul> * <li> The type is an interface type and not an annotation type, enum, or class. * <li> The annotated type satisfies the requirements of a functional interface. * </ul>
    • 編譯器會自動把滿足function interface要求的接口自動識別為function interface,所以你才不需要對上面示例中的 ITest接口增加@FunctionInterface注解。

    * <p>However, the compiler will treat any interface meeting the * definition of a functional interface as a functional interface * regardless of whether or not a {@code FunctionalInterface} * annotation is present on the interface declaration.

    通過了解function interface我們能夠知道怎么才能正確的創建一個function interface來做lambda表達式了。接下來的是了解java是怎么把一個函數當做一個對象作為參數使用的。

    5. 穿越:對象變身函數

    讓我們重新復盤一下上面最開始的實例:

    new Thread(() -> System.out.print("hello world")).start();

    我們知道在jdk8以前我們都是這樣來執行的:

    Runnable r = new Runnable(){System.out.print("hello world"); }; new Thread(r).start();

    我們知道兩者是等價的,也就是說 r 等價于?()->System.out.print("hello world"),一個接口對象等于一個lambda表達式?那么lambda表達式肯定做了這些事情(未看任何資料,純粹推理,有誤再改正):

  • 創建接口對象

  • 實現接口對象

  • 返回接口對象

  • 6. 關于?UnaryOperator

    上篇文章(聊一聊JavaFx中的TextFormatter以及一元操作符UnaryOperator)關于 UnaryOperator草草收尾,在這里給大家重新梳理一下,關于它的使用場景以及它與lambda表達式的關系。

    • 使用場景

    要先理解它的作用,它是接受一個參數并返回與該類型同的值,來看一個List怎么用它的,java.util.List中的replaceAll就用它了:

    ?

    default void replaceAll(UnaryOperator<E> operator) {Objects.requireNonNull(operator);final ListIterator<E> li = this.listIterator();while (li.hasNext()) {li.set(operator.apply(li.next()));}}

    我們可以看到這個方法的目的是把list中的值經過operator操作后重新返回一個新值,例如具體調用。

    ?

    List<String> list = new ArrayList<>();list.add("abc");list.replaceAll(s -> s + "efg");System.out.println(list);

    其中lambda表達式 s->s+"efg"就是這個operator對象,那么最終list中的值就變成了["abcefg"],由此我們可以知道它的作用就是對輸入的值再加工,并返回同類型的值,怎么用就需要你自己擴展發揮了。

    • 與lambda表達式的關系?

    在我看來,它跟lambda表達式的關系并不大,只是它是jdk內置的一種標準操作,類似的二元操作符 BinaryOperator它可以接受兩個同類型參數,并返回同類型參數的值。

    7. 關于UnaryOperator,我們百尺竿頭更進一步,深入到核心

    先貼出它的源碼:

    @FunctionalInterface public interface UnaryOperator<T> extends Function<T, T> {/*** Returns a unary operator that always returns its input argument.** @param <T> the type of the input and output of the operator* @return a unary operator that always returns its input argument*/static <T> UnaryOperator<T> identity() {return t -> t;}}

    我們看到這個function interface居然沒有抽象方法,不,不是沒有,我們繼續看Function接口。

    @FunctionalInterface public interface Function<T, R> {/*** Applies this function to the given argument.** @param t the function argument* @return the function result*/R apply(T t);/*** Returns a composed function that first applies the {@code before}* function to its input, and then applies this function to the result.* If evaluation of either function throws an exception, it is relayed to* the caller of the composed function.** @param <V> the type of input to the {@code before} function, and to the* ? ? ? ? ? composed function* @param before the function to apply before this function is applied* @return a composed function that first applies the {@code before}* function and then applies this function* @throws NullPointerException if before is null** @see #andThen(Function)*/default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {Objects.requireNonNull(before);return (V v) -> apply(before.apply(v));}/*** Returns a composed function that first applies this function to* its input, and then applies the {@code after} function to the result.* If evaluation of either function throws an exception, it is relayed to* the caller of the composed function.** @param <V> the type of output of the {@code after} function, and of the* ? ? ? ? ? composed function* @param after the function to apply after this function is applied* @return a composed function that first applies this function and then* applies the {@code after} function* @throws NullPointerException if after is null** @see #compose(Function)*/default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {Objects.requireNonNull(after);return (T t) -> after.apply(apply(t));}/*** Returns a function that always returns its input argument.** @param <T> the type of the input and output objects to the function* @return a function that always returns its input argument*/static <T> Function<T, T> identity() {return t -> t;}}

    既然他們都被注解為?@FunctionInterface了,那么他們肯定有一個唯一的抽象方法,那就是 apply。

    我們知道?->lambda表達式它是不需要關心函數名字的,所以不管它叫什么, apply也好, apply1也好都可以,但jdk肯定要叫一個更加合理的名字,那么我們知道 s->s+"efg"中?->調用的就是 apply方法。

    而且我們注意到這里有一個 identity()的靜態方法,它返回一個Function對象,它其實跟lambda表達式關系也不大,它的作用是返回當前function所要表達的lambda含義。相當于創建了一個自身對象。

    Function算是lambda的一種擴展應用,這個Function的的作用,是 Representsafunctionthat accepts one argumentandproduces a result.意思是接受一個參數,并產生(返回)一個結果(類型可不同)。

    類似的還有很多Function,都在包java.util.Function中。

    你也可以創建自己的Function,它是用來表達操作是怎樣的。如傳入的參數是什么,返回的是什么。

    其實你只要明白它抽象的是操作就可以了。

    到此就知道,原來UnaryOperator沒啥神秘的,jdk把這些操作放在java.util.function中也正說明了它是一個工具類,是為了提取重復代碼,讓它可以重用,畢竟需要用到這樣的操作的地方太多了,提取是有必要的。

    總結

    以上是生活随笔為你收集整理的你真的了解 lambda 吗(纠错篇)?的全部內容,希望文章能夠幫你解決所遇到的問題。

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