<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                ## 函數式接口 方法引用和 Lambda 表達式都必須被賦值,同時賦值需要類型信息才能使編譯器保證類型的正確性。尤其是Lambda 表達式,它引入了新的要求。 代碼示例: ```java x -> x.toString() ``` 我們清楚這里返回類型必須是 **String**,但 `x` 是什么類型呢? Lambda 表達式包含類型推導(編譯器會自動推導出類型信息,避免了程序員顯式地聲明)。編譯器必須能夠以某種方式推導出 `x` 的類型。 下面是第二個代碼示例: ```java (x, y) -> x + y ``` 現在 `x` 和 `y` 可以是任何支持 `+` 運算符連接的數據類型,可以是兩個不同的數值類型或者是 一個 **String** 加任意一種可自動轉換為 **String** 的數據類型(這包括了大多數類型)。 但是,當 Lambda 表達式被賦值時,編譯器必須確定 `x` 和 `y` 的確切類型以生成正確的代碼。 該問題也適用于方法引用。 假設你要傳遞 `System.out :: println` 到你正在編寫的方法 ,你怎么知道傳遞給方法的參數的類型? 為了解決這個問題,Java 8 引入了 `java.util.function` 包。它包含一組接口,這些接口是 Lambda 表達式和方法引用的目標類型。 每個接口只包含一個抽象方法,稱為函數式方法。 在編寫接口時,可以使用 `@FunctionalInterface` 注解強制執行此“函數式方法”模式: ```java // functional/FunctionalAnnotation.java @FunctionalInterface interface Functional { String goodbye(String arg); } interface FunctionalNoAnn { String goodbye(String arg); } /* @FunctionalInterface interface NotFunctional { String goodbye(String arg); String hello(String arg); } 產生錯誤信息: NotFunctional is not a functional interface multiple non-overriding abstract methods found in interface NotFunctional */ public class FunctionalAnnotation { public String goodbye(String arg) { return "Goodbye, " + arg; } public static void main(String[] args) { FunctionalAnnotation fa = new FunctionalAnnotation(); Functional f = fa::goodbye; FunctionalNoAnn fna = fa::goodbye; // Functional fac = fa; // Incompatible Functional fl = a -> "Goodbye, " + a; FunctionalNoAnn fnal = a -> "Goodbye, " + a; } } ``` `@FunctionalInterface` 注解是可選的; Java 在 `main()` 中把 **Functional** 和 **FunctionalNoAnn** 都當作函數式接口。 在 `NotFunctional` 的定義中可看到`@FunctionalInterface` 的作用:接口中如果有多個抽象方法則會產生編譯期錯誤。 仔細觀察在定義 `f` 和 `fna` 時發生了什么。 `Functional` 和 `FunctionalNoAnn` 定義接口,然而被賦值的只是方法 `goodbye()`。首先,這只是一個方法而不是類;其次,它甚至都不是實現了該接口的類中的方法。這是添加到Java 8中的一點小魔法:如果將方法引用或 Lambda 表達式賦值給函數式接口(類型需要匹配),Java 會適配你的賦值到目標接口。 編譯器會在后臺把方法引用或 Lambda 表達式包裝進實現目標接口的類的實例中。 盡管 `FunctionalAnnotation` 確實適合 `Functional` 模型,但 Java不允許我們像`fac`定義中的那樣,將 `FunctionalAnnotation` 直接賦值給 `Functional`,因為 `FunctionalAnnotation` 并沒有顯式地去實現 `Functional` 接口。唯一的驚喜是,Java 8 允許我們將函數賦值給接口,這樣的語法更加簡單漂亮。 `java.util.function` 包旨在創建一組完整的目標接口,使得我們一般情況下不需再定義自己的接口。主要因為基本類型的存在,導致預定義的接口數量有少許增加。 如果你了解命名模式,顧名思義就能知道特定接口的作用。 以下是基本命名準則: 1. 如果只處理對象而非基本類型,名稱則為 `Function`,`Consumer`,`Predicate` 等。參數類型通過泛型添加。 2. 如果接收的參數是基本類型,則由名稱的第一部分表示,如 `LongConsumer`,`DoubleFunction`,`IntPredicate` 等,但返回基本類型的 `Supplier` 接口例外。 3. 如果返回值為基本類型,則用 `To` 表示,如 `ToLongFunction <T>` 和 `IntToLongFunction`。 4. 如果返回值類型與參數類型一致,則是一個運算符:單個參數使用 `UnaryOperator`,兩個參數使用 `BinaryOperator`。 5. 如果接收兩個參數且返回值為布爾值,則是一個謂詞(Predicate)。 6. 如果接收的兩個參數類型不同,則名稱中有一個 `Bi`。 下表描述了 `java.util.function` 中的目標類型(包括例外情況): | **特征** |**函數式方法名**|**示例**| | :---- | :----: | :----: | |無參數; <br> 無返回值|**Runnable** <br> (java.lang) <br> `run()`|**Runnable**| |無參數; <br> 返回類型任意|**Supplier** <br> `get()` <br> `getAs類型()`| **Supplier`<T>` <br> BooleanSupplier <br> IntSupplier <br> LongSupplier <br> DoubleSupplier**| |無參數; <br> 返回類型任意|**Callable** <br> (java.util.concurrent) <br> `call()`|**Callable`<V>`**| |1 參數; <br> 無返回值|**Consumer** <br> `accept()`|**`Consumer<T>` <br> IntConsumer <br> LongConsumer <br> DoubleConsumer**| |2 參數 **Consumer**|**BiConsumer** <br> `accept()`|**`BiConsumer<T,U>`**| |2 參數 **Consumer**; <br> 1 引用; <br> 1 基本類型|**Obj類型Consumer** <br> `accept()`|**`ObjIntConsumer<T>` <br> `ObjLongConsumer<T>` <br> `ObjDoubleConsumer<T>`**| |1 參數; <br> 返回類型不同|**Function** <br> `apply()` <br> **To類型** 和 **類型To類型** <br> `applyAs類型()`|**Function`<T,R>` <br> IntFunction`<R>` <br> `LongFunction<R>` <br> DoubleFunction`<R>` <br> ToIntFunction`<T>` <br> `ToLongFunction<T>` <br> `ToDoubleFunction<T>` <br> IntToLongFunction <br> IntToDoubleFunction <br> LongToIntFunction <br> LongToDoubleFunction <br> DoubleToIntFunction <br> DoubleToLongFunction**| |1 參數; <br> 返回類型相同|**UnaryOperator** <br> `apply()`|**`UnaryOperator<T>` <br> IntUnaryOperator <br> LongUnaryOperator <br> DoubleUnaryOperator**| |2 參數類型相同; <br> 返回類型相同|**BinaryOperator** <br> `apply()`|**`BinaryOperator<T>` <br> IntBinaryOperator <br> LongBinaryOperator <br> DoubleBinaryOperator**| |2 參數類型相同; <br> 返回整型|Comparator <br> (java.util) <br> `compare()`|**`Comparator<T>`**| |2 參數; <br> 返回布爾型|**Predicate** <br> `test()`|**`Predicate<T>` <br> `BiPredicate<T,U>` <br> IntPredicate <br> LongPredicate <br> DoublePredicate**| |參數基本類型; <br> 返回基本類型|**類型To類型Function** <br> `applyAs類型()`|**IntToLongFunction <br> IntToDoubleFunction <br> LongToIntFunction <br> LongToDoubleFunction <br> DoubleToIntFunction <br> DoubleToLongFunction**| |2 參數類型不同|**Bi操作** <br> (不同方法名)|**`BiFunction<T,U,R>` <br> `BiConsumer<T,U>` <br> `BiPredicate<T,U>` <br> `ToIntBiFunction<T,U>` <br> `ToLongBiFunction<T,U>` <br> `ToDoubleBiFunction<T>`**| 此表僅提供些常規方案。通過上表,你應該或多或少能自行推導出你所需要的函數式接口。 可以看出,在創建 `java.util.function` 時,設計者們做出了一些選擇。 例如,為什么沒有 `IntComparator`,`LongComparator` 和 `DoubleComparator` 呢?有 `BooleanSupplier` 卻沒有其他表示 **Boolean** 的接口;有通用的 `BiConsumer` 卻沒有用于 **int**,**long** 和 **double** 的 `BiConsumers` 變體(我理解他們為什么放棄這些接口)。這到底是疏忽還是有人認為其他組合使用得很少呢(他們是如何得出這個結論的)? 你還可以看到基本類型給 Java 添加了多少復雜性。基于效率方面的考慮(問題之后有所緩解),該語言的第一版中就包含了基本類型。現在,在語言的生命周期中,我們仍然會受到語言設計選擇不佳的影響。 下面枚舉了基于 Lambda 表達式的所有不同 **Function** 變體的示例: ```java // functional/FunctionVariants.java import java.util.function.*; class Foo {} class Bar { Foo f; Bar(Foo f) { this.f = f; } } class IBaz { int i; IBaz(int i) { this.i = i; } } class LBaz { long l; LBaz(long l) { this.l = l; } } class DBaz { double d; DBaz(double d) { this.d = d; } } public class FunctionVariants { static Function<Foo,Bar> f1 = f -> new Bar(f); static IntFunction<IBaz> f2 = i -> new IBaz(i); static LongFunction<LBaz> f3 = l -> new LBaz(l); static DoubleFunction<DBaz> f4 = d -> new DBaz(d); static ToIntFunction<IBaz> f5 = ib -> ib.i; static ToLongFunction<LBaz> f6 = lb -> lb.l; static ToDoubleFunction<DBaz> f7 = db -> db.d; static IntToLongFunction f8 = i -> i; static IntToDoubleFunction f9 = i -> i; static LongToIntFunction f10 = l -> (int)l; static LongToDoubleFunction f11 = l -> l; static DoubleToIntFunction f12 = d -> (int)d; static DoubleToLongFunction f13 = d -> (long)d; public static void main(String[] args) { Bar b = f1.apply(new Foo()); IBaz ib = f2.apply(11); LBaz lb = f3.apply(11); DBaz db = f4.apply(11); int i = f5.applyAsInt(ib); long l = f6.applyAsLong(lb); double d = f7.applyAsDouble(db); l = f8.applyAsLong(12); d = f9.applyAsDouble(12); i = f10.applyAsInt(12); d = f11.applyAsDouble(12); i = f12.applyAsInt(13.0); l = f13.applyAsLong(13.0); } } ``` 這些 Lambda 表達式嘗試生成適合函數簽名的最簡代碼。 在某些情況下,有必要進行強制類型轉換,否則編譯器會報截斷錯誤。 主方法中的每個測試都顯示了 `Function` 接口中不同類型的 `apply()` 方法。 每個都產生一個與其關聯的 Lambda 表達式的調用。 方法引用有自己的小魔法: ```java / functional/MethodConversion.java import java.util.function.*; class In1 {} class In2 {} public class MethodConversion { static void accept(In1 i1, In2 i2) { System.out.println("accept()"); } static void someOtherName(In1 i1, In2 i2) { System.out.println("someOtherName()"); } public static void main(String[] args) { BiConsumer<In1,In2> bic; bic = MethodConversion::accept; bic.accept(new In1(), new In2()); bic = MethodConversion::someOtherName; // bic.someOtherName(new In1(), new In2()); // Nope bic.accept(new In1(), new In2()); } } ``` 輸出結果: ``` accept() someOtherName() ``` 查看 `BiConsumer` 的文檔,你會看到 `accept()` 方法。 實際上,如果我們將方法命名為 `accept()`,它就可以作為方法引用。 但是我們也可用不同的名稱,比如 `someOtherName()`。只要參數類型、返回類型與 `BiConsumer` 的 `accept()` 相同即可。 因此,在使用函數接口時,名稱無關緊要——只要參數類型和返回類型相同。 Java 會將你的方法映射到接口方法。 要調用方法,可以調用接口的函數式方法名(在本例中為 `accept()`),而不是你的方法名。 現在我們來看看,將方法引用應用于基于類的函數式接口(即那些不包含基本類型的函數式接口)。下面的例子中,我創建了適合函數式方法簽名的最簡單的方法: ```java // functional/ClassFunctionals.java import java.util.*; import java.util.function.*; class AA {} class BB {} class CC {} public class ClassFunctionals { static AA f1() { return new AA(); } static int f2(AA aa1, AA aa2) { return 1; } static void f3(AA aa) {} static void f4(AA aa, BB bb) {} static CC f5(AA aa) { return new CC(); } static CC f6(AA aa, BB bb) { return new CC(); } static boolean f7(AA aa) { return true; } static boolean f8(AA aa, BB bb) { return true; } static AA f9(AA aa) { return new AA(); } static AA f10(AA aa1, AA aa2) { return new AA(); } public static void main(String[] args) { Supplier<AA> s = ClassFunctionals::f1; s.get(); Comparator<AA> c = ClassFunctionals::f2; c.compare(new AA(), new AA()); Consumer<AA> cons = ClassFunctionals::f3; cons.accept(new AA()); BiConsumer<AA,BB> bicons = ClassFunctionals::f4; bicons.accept(new AA(), new BB()); Function<AA,CC> f = ClassFunctionals::f5; CC cc = f.apply(new AA()); BiFunction<AA,BB,CC> bif = ClassFunctionals::f6; cc = bif.apply(new AA(), new BB()); Predicate<AA> p = ClassFunctionals::f7; boolean result = p.test(new AA()); BiPredicate<AA,BB> bip = ClassFunctionals::f8; result = bip.test(new AA(), new BB()); UnaryOperator<AA> uo = ClassFunctionals::f9; AA aa = uo.apply(new AA()); BinaryOperator<AA> bo = ClassFunctionals::f10; aa = bo.apply(new AA(), new AA()); } } ``` 請**注意**,每個方法名稱都是隨意的(如 `f1()`,`f2()`等)。正如你剛才看到的,一旦將方法引用賦值給函數接口,我們就可以調用與該接口關聯的函數方法。 在此示例中為 `get()`、`compare()`、`accept()`、`apply()` 和 `test()`。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看