1. 程式人生 > >Java 8方法引用使用指南

Java 8方法引用使用指南

【編者按】本文作者為擁有15年 Java 開發經驗的資深程式設計師 Per-Åke Minborg,主要介紹如何靈活地解析 Java 中的方法引用。文章系國內 ITOM 管理平臺 OneAPM 編譯呈現。

方法引用

眾所周知,在Java 8中我們可以使用方法引用。譬如,在我們需要遍歷流元素時,可以使用 String::isEmpty 來引用isEmpty方法。試看下面這段程式碼:

Stream.of("A", "", "B").filter(Stream::isEmpty).count();

執行的結果為1(因為在這個流中只有一個空元素)。但是,如果我們要過濾出非空字串,我們得寫成.filter(s -> !s.isEmpty())

。這是一個Lambda表示式。顯然,這兒有個討厭的不對稱想象。我們可以使用方法引用,但卻不能用它的反式。我們可以寫predicate.negate()卻不能寫Stream::isEmpty.negate()!Stream::isEmpty

為什麼呢?這是因為方法引用並非Lambda表示式或者函式介面。不過,使用Java的型別推導可以將方法引用解析為一個或多個函式介面。上例中的String::isEmpty至少可以解析為:

Predicate<String>
Function<String, Boolean>

所以,我們要排除其他可能性,確定到底將方法引用轉換為哪個函式介面。本文在一定程度上解決了該問題。文中的程式碼來自開源專案

Speedment,它讓資料庫看起來像Java 8的流。

解析方法引用

其實,以靜態方法為“管道”,可以部分地解決這個問題——該靜態方法以一個方法引用為輸入,以特定的函式介面為其返回。試考慮下面這個簡短的靜態方法:

public static <T> Predicate<T> as(Predicate<T> predicate) {
    return predicate;
}

現在,如果靜態地匯入這個方法,事實上,我們就能更簡單地使用方法引用。如下例所示:

Stream.of("A", "", "B").filter(as(String::isEmpty).negate()).count();

這段程式碼返回的結果為2,即流中非空元素的數量。有關方法引用的使用方式,我們又向前邁進了一步。另一個好處是,這個解決方案讓我們更輕鬆地編寫predicates介面,例如:

.filter(as(String::isEmpty).negate().and("A"::equals))

解析所有方法引用

但是,現在仍有一個問題亟待解決。我們不能隨隨便便地建立一大堆靜態as()函式,因為一個方法引用可能解析為多種as()方法,正如本文開頭提到的那樣。所以,一個更妙的解決方案,是把函式介面型別名新增至每個靜態方法,這樣我們就可以程式化地為每個函式介面轉換方法選擇一個特定的方法引用。我們有一個工具類,可以讓每個方法引用都轉換為Java標準包 `java.util.function中任意匹配的函式介面。

直接在GitHub下載最新版本

import java.util.function.*;
/**
 *
 * @author Per Minborg
 */
public class FunctionCastUtil {
    public static <T, U> BiConsumer<T, U> asBiConsumer(BiConsumer<T, U> biConsumer) {
        return biConsumer;
    }
    public static <T, U, R> BiFunction<T, U, R> asBiFunction(BiFunction<T, U, R> biFunction) {
        return biFunction;
    }
    public static <T> BinaryOperator<T> asBinaryOperator(BinaryOperator<T> binaryOperator) {
        return binaryOperator;
    }
    public static <T, U> BiPredicate<T, U> asBiPredicate(BiPredicate<T, U> biPredicate) {
        return biPredicate;
    }
    public static BooleanSupplier asBooleanSupplier(BooleanSupplier booleanSupplier) {
        return booleanSupplier;
    }
    public static <T> Consumer<T> asConsumer(Consumer<T> consumer) {
        return consumer;
    }
    public static DoubleBinaryOperator asDoubleBinaryOperator(DoubleBinaryOperator doubleBinaryOperator) {
        return doubleBinaryOperator;
    }
    public static DoubleConsumer asDoubleConsumer(DoubleConsumer doubleConsumer) {
        return doubleConsumer;
    }
    public static <R> DoubleFunction<R> asDoubleFunction(DoubleFunction<R> doubleFunction) {
        return doubleFunction;
    }
    public static DoublePredicate asDoublePredicate(DoublePredicate doublePredicate) {
        return doublePredicate;
    }
    public static DoubleToIntFunction asDoubleToIntFunction(DoubleToIntFunction doubleToIntFunctiontem) {
        return doubleToIntFunctiontem;
    }
    public static DoubleToLongFunction asDoubleToLongFunction(DoubleToLongFunction doubleToLongFunction) {
        return doubleToLongFunction;
    }
    public static DoubleUnaryOperator asDoubleUnaryOperator(DoubleUnaryOperator doubleUnaryOperator) {
        return doubleUnaryOperator;
    }
    public static <T, R> Function<T, R> asFunction(Function<T, R> function) {
        return function;
    }
    public static IntBinaryOperator asIntBinaryOperator(IntBinaryOperator intBinaryOperator) {
        return intBinaryOperator;
    }
    public static IntConsumer asIntConsumer(IntConsumer intConsumer) {
        return intConsumer;
    }
    public static <R> IntFunction<R> asIntFunction(IntFunction<R> intFunction) {
        return intFunction;
    }
    public static IntPredicate asIntPredicate(IntPredicate intPredicate) {
        return intPredicate;
    }
    public static IntSupplier asIntSupplier(IntSupplier intSupplier) {
        return intSupplier;
    }
    public static IntToDoubleFunction asIntToDoubleFunction(IntToDoubleFunction intToDoubleFunction) {
        return intToDoubleFunction;
    }
    public static IntToLongFunction asIntToLongFunction(IntToLongFunction intToLongFunction) {
        return intToLongFunction;
    }
    public static IntUnaryOperator asIntUnaryOperator(IntUnaryOperator intUnaryOperator) {
        return intUnaryOperator;
    }
    public static LongBinaryOperator asLongBinaryOperator(LongBinaryOperator longBinaryOperator) {
        return longBinaryOperator;
    }
    public static LongConsumer asLongConsumer(LongConsumer longConsumer) {
        return longConsumer;
    }
    public static <R> LongFunction<R> asLongFunction(LongFunction<R> longFunction) {
        return longFunction;
    }
    public static LongPredicate asLongPredicate(LongPredicate longPredicate) {
        return longPredicate;
    }
    public static <T> LongSupplier asLongSupplier(LongSupplier longSupplier) {
        return longSupplier;
    }
    public static LongToDoubleFunction asLongToDoubleFunction(LongToDoubleFunction longToDoubleFunction) {
        return longToDoubleFunction;
    }
    public static LongToIntFunction asLongToIntFunction(LongToIntFunction longToIntFunction) {
        return longToIntFunction;
    }
    public static LongUnaryOperator asLongUnaryOperator(LongUnaryOperator longUnaryOperator) {
        return longUnaryOperator;
    }
    public static <T> ObjDoubleConsumer<T> asObjDoubleConsumer(ObjDoubleConsumer<T> objDoubleConsumer) {
        return objDoubleConsumer;
    }
    public static <T> ObjIntConsumer<T> asObjIntConsumer(ObjIntConsumer<T> objIntConsumer) {
        return objIntConsumer;
    }
    public static <T> ObjLongConsumer<T> asObjLongConsumer(ObjLongConsumer<T> objLongConsumer) {
        return objLongConsumer;
    }
    public static <T> Predicate<T> asPredicate(Predicate<T> predicate) {
        return predicate;
    }
    public static <T> Supplier<T> asSupplier(Supplier<T> supplier) {
        return supplier;
    }
    public static <T, U> ToDoubleBiFunction<T, U> asToDoubleBiFunction(ToDoubleBiFunction<T, U> toDoubleBiFunction) {
        return toDoubleBiFunction;
    }
    public static <T> ToDoubleFunction<T> asToDoubleFunction(ToDoubleFunction<T> toDoubleFunction) {
        return toDoubleFunction;
    }
    public static <T, U> ToIntBiFunction<T, U> asToIntBiFunction(ToIntBiFunction<T, U> toIntBiFunction) {
        return toIntBiFunction;
    }
    public static <T> ToIntFunction<T> asToIntFunction(ToIntFunction<T> ioIntFunction) {
        return ioIntFunction;
    }
    public static <T, U> ToLongBiFunction<T, U> asToLongBiFunction(ToLongBiFunction<T, U> toLongBiFunction) {
        return toLongBiFunction;
    }
    public static <T> ToLongFunction<T> asToLongFunction(ToLongFunction<T> toLongFunction) {
        return toLongFunction;
    }
    public static <T> UnaryOperator<T> asUnaryOperator(UnaryOperator<T> unaryOperator) {
        return unaryOperator;
    }
    private FunctionCastUtil() {
    }
}

在靜態匯入了相關方法之後,我們就可以這樣寫:

Stream.of("A", "", "B").filter(asPredicate(String::isEmpty).negate()).count();

一個更好的解決方案

如果函式介面本身就包含一個接收方法引用並將其轉換為某類函式介面的靜態方法,那就更好了。舉例來說,標準的Java Predicated函式介面就會變成這樣:

@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);
    default Predicate<T> and(Predicate<? super T> other) {...}
    default Predicate<T> negate() {...}
    default Predicate<T> or(Predicate<? super T> other) {...}
    static <T> Predicate<T> isEqual(Object targetRef) {...}
    // New proposed support method to return a 
    // Predicate view of a Functional Reference 
    public static <T> Predicate<T> of(Predicate<T> predicate) {
        return predicate;
    }
}

因此,我們可以這樣寫:

Stream.of("A", "", "B").filter(Predicate.of(String::isEmpty).negate()).count();

筆者覺得這樣看起來好極了!

快聯絡離你最近的Open JDK開發人員,提出你的修改建議吧!

OneAPM 能為您提供端到端的 Java 應用效能解決方案,我們支援所有常見的 Java 框架及應用伺服器,助您快速發現系統瓶頸,定位異常根本原因。分鐘級部署,即刻體驗,Java 監控從來沒有如此簡單。想閱讀更多技術文章,請訪問 OneAPM 官方技術部落格

相關推薦

Java 8方法引用使用指南

【編者按】本文作者為擁有15年 Java 開發經驗的資深程式設計師 Per-Åke Minborg,主要介紹如何靈活地解析 Java 中的方法引用。文章系國內 ITOM 管理平臺 OneAPM 編譯呈現。 方法引用 眾所周知,在Java 8中我們可以使用

Java 8 方法引用

bar ria john 點擊 targe ati void linda icu Java 8 方法引用 There are four kinds of method references: Kind

Java 8 ------------方法引用

在Java8中,我們可以直接通過方法引用來簡寫lambda表示式中已經存在的方法。 Arrays.sort(stringsArray, String::compareToIgnoreCase); 這種特性就叫做方法引用(Method Reference)。 方法引用的形式 方法引用的標準

Java 8 特性 – 終極指南

第一次嘗試翻譯文章,有錯誤請見諒:) 編者注:Java 8出現在公眾視野中已經有一段時間了,在這期間,種種跡象都表明Java 8是一個非常重要的版本。 為了您閱讀的便利,我們把Java 8主要特性都收集起來放在本文中,希望您能喜歡。 目錄 1. 導言 2.1. Lambdas 和 函式介面 2.2

Java 8 Optional 良心指南,建議收藏

想學習,永遠都不晚,尤其是針對 Java 8 裡面的好東西,Optional 就是其中之一,該類提供了一種用於表示可選值而非空引用的類級別解決方案。作為一名 Java 程式設計師,我真的是煩透了 NullPointerException(NPE),儘管和它熟得就像一位老朋友,知道它也是迫不得已——程式正在使用

Java 8 中的方法引用

時間 情況 arrays 抽象 以及 eth ted 方式 消費 一、原理概要 lambda 表示式,可以作為某些匿名內部類的替代。主要目的是調用該內部類中的方法,而該方法的實現(重寫)由 lambda表示式決定。 通常,我們可能不關心匿名內部類中的具體方法(被重寫的方法)

淺談Java 8中的方法引用(Method References)

  本人接觸Java 8的時間不長,對Java 8的一些新特性略有所知。Java 8引入了一些新的程式設計概念,比如經常用到的 lambda表示式、Stream、Optional以及Function等,讓人耳目一新。這些功能其實上手並不是很難,根據別人的程式碼抄過來改一下,並不要知道內部的實現原理,也可以很熟

Java 8 In Action之引用特定型別的任意物件的例項方法

此種引用型別名稱原文為:reference to an instance method of an arbitrary object of a particular type 今天在和同學討論另外一個問題的時候(直接導致這個問題只有明天再解決了),突然爭論到能不能用類來呼叫

Java 8方法引用(Method References)

Java 8中方法也是一種物件,可以By名字來引用。不過方法引用的唯一用途是支援Lambda的簡寫,使用方法名稱來表示Lambda。不能通過方法引用來獲得諸如方法簽名的相關資訊。 方法引用的分類 方法引用分為4類,常用的是前兩種。方法引用也受到訪問控制權限的

Java 8 新特性:Lambda 表示式之方法引用(Lambda 表示式補充版)

方法引用 文 | 莫若吻      (注:此文乃個人查詢資料然後學習總結的,若有不對的地方,請大家指出,非常感謝!) 1.方法引用簡述 方法引用是用來直接訪問類或者例項的已經存在的方法或

Java 8 Lambda表達式之方法引用 ::雙冒號操作符

ont tag shm 表達 類型 beta add nta collected 雙冒號運算符就是java中的方法引用,方法引用的格式是類名::方法名。 這裏只是方法名,方法名的後面沒有括號“()”。--------> 這樣的式子並不代表一定會調用這個

Java 8 新特性———方法引用和構造器引用

1.方法引用 當要傳遞給Lambda體的操作,已經有實現的方法了,可以使用方法引用!(實現抽象方法的引數列表,必須與方法引用方法的引數列表保持一致!)方法引用:使用操作符“::” 將方法名和物件或類的名字分隔開來。 如下三種主要使用情況: 物件::例項方法 類::靜態方

java 8新特性之方法引用

方法引用通過方法的名字來指向一個方法。 方法引用可以使語言的構造更緊湊簡潔,減少冗餘程式碼。 方法引用使用一對冒號 :: 。 下面,我們在 Car 類中定義了 4 個方法作為例子來區分 Java 中 4 種不同方法的引用。 public class Car {

Java 8 中的方法引用,輕鬆減少程式碼量,提升可讀性!

## 1. 引言 Java8中最受廣大開發中喜歡的變化之一是因為引入了 lambda 表示式,因為這些表示式允許我們放棄匿名類,從而大大減少了樣板程式碼,並提高了可讀性。 **方法引用是lambda表示式的一種特殊型別**。它們通常通過引用現有方法來建立簡單的lambda表示式。 方法引用包括以下四種類型

Effective Java 第三版——43.方法引用優於lambda表達式

incr ges ren 常用 eem 占用 pre 我們 his Tips 《Effective Java, Third Edition》一書英文版已經出版,這本書的第二版想必很多人都讀過,號稱Java四大名著之一,不過第二版2009年出版,到現在已經將近8年的時間,但

Java 8——接口中個的默認方法和靜態方法

string 深入 col 函數 cti code 引用 lan mov 在Java SE 8之前,interface只是事物的抽象,用來定義統一的抽象事物和描述事物的抽象行為和屬性。 但是在Java SE 8中,增加了可以在interface中增加默認實現的行為和事物的靜

Java學習——方法中傳遞參數分簡單類型與復雜類型(引用類型)編程計算100+98+96+。。。+4+2+1的值,用遞歸方法實現

dig oid 傳遞 system alt style 類型 遞歸 gen package hello; public class digui { public static void main(String[] args) { /

Java四種方法引用

方法引用是lambda表示式的一種特殊形式,如果正好有某個方法滿足一個lambda表示式的形式,那就可以將這個lambda表示式用方法引用的方式表示,但是如果這個lambda表示式的比較複雜就不能用方法引用進行替換。實際上方法引用是lambda表示式的一種語法糖。在介紹方法引用使用方式之前,先將方法

lambda表示式之方法引用----java

1.概念 ---- 什麼是方法引用???     對於每一個java類來說,它們都主要有三種方法,即普通方法、靜態方法和構造方法。而方法引用就是利用函式式介面+lambda表示式(這裡的lambda表示式並非前面提到的帶"->“符號的表示式,而是使用雙冒號”::

java方法引用(Method References)

java世界的變化有點大,java8之後大家都在寫lambdas表示式,感覺方法引用也是為了支援這一特點吧(當然也看到一些人人說沒什麼用)。 看到這麼一段程式碼,是不是很清晰地解釋了了不同寫法的區別。 可以看到使用雙冒號(::)將例項引用或類名與方法分開。 Iterable介面的foreash方法傳入C