Java 8方法引用使用指南

【編者按】本文作者為擁有15年 Java 開發(fā)經(jīng)驗(yàn)的資深程序員 Per-?ke Minborg,主要介紹如何靈活地解析 Java 中的方法引用。文章系國內(nèi) ITOM 管理平臺(tái) OneAPM 編譯呈現(xiàn)。

方法引用

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

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

運(yùn)行的結(jié)果為1(因?yàn)樵谶@個(gè)流中只有一個(gè)空元素)。但是,如果我們要過濾出非空字符串,我們得寫成.filter(s -> !s.isEmpty())。這是一個(gè)Lambda表達(dá)式。顯然,這兒有個(gè)討厭的不對(duì)稱想象。我們可以使用方法引用,但卻不能用它的反式。我們可以寫predicate.negate()卻不能寫Stream::isEmpty.negate()!Stream::isEmpty。

為什么呢?這是因?yàn)榉椒ㄒ貌⒎荓ambda表達(dá)式或者函數(shù)接口。不過,使用Java的類型推導(dǎo)可以將方法引用解析為一個(gè)或多個(gè)函數(shù)接口。上例中的String::isEmpty至少可以解析為:

Predicate<String>
Function<String, Boolean>

所以,我們要排除其他可能性,確定到底將方法引用轉(zhuǎn)換為哪個(gè)函數(shù)接口。本文在一定程度上解決了該問題。文中的代碼來自開源項(xiàng)目Speedment,它讓數(shù)據(jù)庫看起來像Java 8的流。

解析方法引用

其實(shí),以靜態(tài)方法為“管道”,可以部分地解決這個(gè)問題——該靜態(tài)方法以一個(gè)方法引用為輸入,以特定的函數(shù)接口為其返回。試考慮下面這個(gè)簡短的靜態(tài)方法:

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

現(xiàn)在,如果靜態(tài)地導(dǎo)入這個(gè)方法,事實(shí)上,我們就能更簡單地使用方法引用。如下例所示:

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

這段代碼返回的結(jié)果為2,即流中非空元素的數(shù)量。有關(guān)方法引用的使用方式,我們又向前邁進(jìn)了一步。另一個(gè)好處是,這個(gè)解決方案讓我們更輕松地編寫predicates接口,例如:

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

解析所有方法引用

但是,現(xiàn)在仍有一個(gè)問題亟待解決。我們不能隨隨便便地創(chuàng)建一大堆靜態(tài)as()函數(shù),因?yàn)橐粋€(gè)方法引用可能解析為多種as()方法,正如本文開頭提到的那樣。所以,一個(gè)更妙的解決方案,是把函數(shù)接口類型名添加至每個(gè)靜態(tài)方法,這樣我們就可以程序化地為每個(gè)函數(shù)接口轉(zhuǎn)換方法選擇一個(gè)特定的方法引用。我們有一個(gè)工具類,可以讓每個(gè)方法引用都轉(zhuǎn)換為Java標(biāo)準(zhǔn)包 `java.util.function中任意匹配的函數(shù)接口。

直接在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() {
    }
}

在靜態(tài)導(dǎo)入了相關(guān)方法之后,我們就可以這樣寫:

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

一個(gè)更好的解決方案

如果函數(shù)接口本身就包含一個(gè)接收方法引用并將其轉(zhuǎn)換為某類函數(shù)接口的靜態(tài)方法,那就更好了。舉例來說,標(biāo)準(zhǔn)的Java Predicated函數(shù)接口就會(huì)變成這樣:

@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();

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

快聯(lián)系離你最近的Open JDK開發(fā)人員,提出你的修改建議吧!

OneAPM 能為您提供端到端的 Java 應(yīng)用性能解決方案,我們支持所有常見的 Java 框架及應(yīng)用服務(wù)器,助您快速發(fā)現(xiàn)系統(tǒng)瓶頸,定位異常根本原因。分鐘級(jí)部署,即刻體驗(yàn),Java 監(jiān)控從來沒有如此簡單。想閱讀更多技術(shù)文章,請(qǐng)?jiān)L問 OneAPM 官方技術(shù)博客。

本文轉(zhuǎn)自 OneAPM 官方博客

原帖地址:https://dzone.com/articles/put-your-java-8-method-references-to-work

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容