成人午夜视频全免费观看高清-秋霞福利视频一区二区三区-国产精品久久久久电影小说-亚洲不卡区三一区三区一区

Java函數(shù)式編程怎么應用

今天小編給大家分享一下Java函數(shù)式編程怎么應用的相關知識點,內(nèi)容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。

成都創(chuàng)新互聯(lián)是一家集網(wǎng)站建設,銀州企業(yè)網(wǎng)站建設,銀州品牌網(wǎng)站建設,網(wǎng)站定制,銀州網(wǎng)站建設報價,網(wǎng)絡營銷,網(wǎng)絡優(yōu)化,銀州網(wǎng)站推廣為一體的創(chuàng)新建站企業(yè),幫助傳統(tǒng)企業(yè)提升企業(yè)形象加強企業(yè)競爭力??沙浞譂M足這一群體相比中小企業(yè)更為豐富、高端、多元的互聯(lián)網(wǎng)需求。同時我們時刻保持專業(yè)、時尚、前沿,時刻以成就客戶成長自我,堅持不斷學習、思考、沉淀、凈化自己,讓我們?yōu)楦嗟钠髽I(yè)打造出實用型網(wǎng)站。

Java 根據(jù)常用需求場景的用例,抽象出了幾個內(nèi)置的函數(shù)式接口給開發(fā)者使用,比如Function、 Supplier 等等,Stream 中各種操作方法的參數(shù)或者是返回值類型往往就是這些內(nèi)置的函數(shù)式接口。

比如 Stream 中 map 操作方法的參數(shù)類型就是 Function

<R> Stream<R> map(Function<? super T, ? extends R> mapper);

那為什么我們在平時使用 Stream 操作的 map 方法時,從來沒有見過聲明這個類型的參數(shù)呢?比如下面這個通過 map 方法把流中的每個元素轉(zhuǎn)換成大寫的例子。

List<String> list = new ArrayList<String>();
Stream<String> stream = list.stream();

Stream<String> streamMapped = stream.map((value) -> value.toUpperCase());

map 方法的參數(shù)直接是一個 Lambada 表達式:

(value) -> value.toUpperCase()

這個Lambda 表達式就是Function接口的實現(xiàn)。

函數(shù)式接口的載體通常是 Lambda 表達式,通過 Lambda 表達式,編譯器會根據(jù) Lambda 表達式的參數(shù)和返回值推斷出其實現(xiàn)的是哪個函數(shù)式接口。使用 Lambda 表達式實現(xiàn)接口,我們不必像匿名內(nèi)部類那樣--指明類要實現(xiàn)的接口,所以像 Stream 操作中雖然參數(shù)或者返回值類型很多都是 Java 的內(nèi)置函數(shù)式接口,但是我們并沒有顯示的使用匿名類實現(xiàn)它們。

雖然Lambda 表達式使用起來很方便,不過這也從側(cè)面造成了咋一看到那些 Java 內(nèi)置的函數(shù)式接口類型時,我們會有點迷惑“這貨是啥?這貨又是啥?”的感覺。

下面我們先說一下函數(shù)式編程、Java 的函數(shù)式接口、Lambda 為什么只能實現(xiàn)函數(shù)式接口這幾個問題,把這些東西搞清楚了再梳理 Java 內(nèi)置提供了哪些函數(shù)式接口。

函數(shù)式編程

函數(shù)式編程中包含以下兩個關鍵的概念:

  • 函數(shù)是第一等公民

  • 函數(shù)要滿足一下約束

    • 函數(shù)的返回值僅取決于傳遞給函數(shù)的輸入?yún)?shù)。

    • 函數(shù)的執(zhí)行沒有副作用。

即使我們在寫程序的時候沒有一直遵循所有這些規(guī)則,但仍然可以從使用函數(shù)式編程思想編寫程序中獲益良多。

接下來,我們來看一下這兩個關鍵概念再 Java 函數(shù)編程中的落地。

函數(shù)是一等公民

在函數(shù)式編程范式中,函數(shù)是語言中的第一等公民。這意味著可以創(chuàng)建函數(shù)的“實例”,對函數(shù)實例的變量引用,就像對字符串、Map 或任何其他對象的引用一樣。函數(shù)也可以作為參數(shù)傳遞給其他函數(shù)。

在 Java 中,函數(shù)顯然不是第一等公民,類才是。所以 Java 才引入 Lambda 表達式,這個語法糖從表現(xiàn)層上讓 Java 擁有了函數(shù),讓函數(shù)可以作為變量的引用、方法的參數(shù)等等。為啥說是從表現(xiàn)層呢?因為實際上在編譯的時候 Java 編譯器還是會把 Lambda 表達式編譯成類。

純函數(shù)

函數(shù)編程中,有個純函數(shù)(Pure Function)的概念,如果一個函數(shù)滿足以下條件,才是純函數(shù):

  • 該函數(shù)的執(zhí)行沒有副作用。

  • 函數(shù)的返回值僅取決于傳遞給函數(shù)的輸入?yún)?shù)。

下面是一個 Java 中的純函數(shù)(方法)示例

public class ObjectWithPureFunction{    public int sum(int a, int b) {        return a + b;
    }
}

上面這個sum()方法的返回值僅取決于其輸入?yún)?shù),而且sum()是沒有副作用的,它不會在任何地方修改函數(shù)之外的任何狀態(tài)(變量)。

相反,這里是一個非純函數(shù)的例子:

public class ObjectWithNonPureFunction{    private int value = 0;    public int add(int nextValue) {        this.value += nextValue;        return this.value;
    }
}

add()方法使用成員變量value來計算其返回值,并且它還修改了value成員變量的狀態(tài),這代表它有副作用,這兩個條件都導致add方法不是一個純函數(shù)

正如我們看到的,函數(shù)式編程并不是解決所有問題的銀彈。尤其是“函數(shù)是沒有副作用的”這個原則就使得在一些場景下很難使用函數(shù)式編程,比如要寫入數(shù)據(jù)庫的場景,寫入數(shù)據(jù)庫就算是一個副作用。所以,我們需要做的是了解函數(shù)式編程擅長解決哪些問題,把它用在正確的地方。

函數(shù)式接口

Java中的函數(shù)式接口在 Lambda 表達式那篇文章里提到過,這里再詳細說說。函數(shù)式接口是只有一個抽象方法的接口(抽象方法即未實現(xiàn)方法體的方法)。一個 Interface 接口中可以有多個方法,其中默認方法和靜態(tài)方法都自帶實現(xiàn),但是只要接口中有且僅有一個方法沒有被實現(xiàn),那么這個接口就可以被看做是一個函數(shù)式接口。

下面這個接口只定義了一個抽象方法,顯然它是一個函數(shù)式接口:

public interface MyInterface {    public void run();
}

下面這個接口中,定義了多個方法,不過它也是一個函數(shù)式接口:

public interface MyInterface2 {
    public void run();

    public default void doIt() {
        System.out.println("doing it");
    }

    public static void doItStatically() {
        System.out.println("doing it statically");
    }
}

因為doIt方法在接口中定義了默認實現(xiàn),靜態(tài)方法也有實現(xiàn),接口中只有一個抽象方法run沒有提供實現(xiàn),所以它滿足函數(shù)式接口的要求。

這里要注意,如果接口中有多個方法沒有被實現(xiàn),那么接口將不再是函數(shù)式接口,因此也就沒辦法用 Java 的 Lambda 表達式實現(xiàn)接口了

編譯器會根據(jù) Lambda 表達式的參數(shù)和返回值類型推斷出其實現(xiàn)的抽象方法,進而推斷出其實現(xiàn)的接口,如果一個接口有多個抽象方法,顯然是沒辦法用 Lambda 表達式實現(xiàn)該接口的。

@FunctionalInterface 注解

這里擴充一個標注接口是函數(shù)式接口的注解@FunctionalInterface

@FunctionalInterface 
// 標明接口為函數(shù)式接口
public interface MyInterface {    public void run(); 
//抽象方法}

一旦使用了該注解標注接口,Java 的編譯器將會強制檢查該接口是否滿足函數(shù)式接口的要求:“確實有且僅有一個抽象方法”,否則將會報錯。

需要注意的是,即使不使用該注解,只要一個接口滿足函數(shù)式接口的要求,那它仍然是一個函數(shù)式接口,使用起來都一樣。該注解只起到--標記接口指示編譯器對其進行檢查的作用。

Java 內(nèi)置的函數(shù)式接口

Java 語言內(nèi)置了一組為常見場景的用例設計的函數(shù)式接口,這樣我們就不必每次用到Lambda 表達式、Stream 操作時先創(chuàng)建函數(shù)式接口了,Java 的接口本身也支持泛型類型,所以基本上 Java 內(nèi)置的函數(shù)式接口就能滿足我們平時編程的需求,我自己在開發(fā)項目時,印象里很少見過有人自定義函數(shù)式接口。

Function

Function接口(全限定名:java.util.function.Function)是Java中最核心的函數(shù)式接口。 Function 接口表示一個接受單個參數(shù)并返回單個值的函數(shù)(方法)。以下是 Function 接口定義的:

@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);

    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }

    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

    static <T> Function<T, T> identity() {
            return t -> t;
    }

Function接口本身只包含一個需要實現(xiàn)的抽象方法apply,其他幾個方法都已在接口中提供了實現(xiàn),這正好符合上面我們講的函數(shù)式接口的定義:“有且僅有一個抽象方法的接口”。

Function 接口中的其他三個方法中compse、andThen 這兩個方法用于函數(shù)式編程的組合調(diào)用,identity用于返回調(diào)用實體對象本身。

Function接口用Java 的類這么實現(xiàn)

public class AddThree implements Function<Long, Long> {

    @Override
    public Long apply(Long aLong) {
        return aLong + 3;
    }

    public static void main(String[] args) {
        Function<Long, Long> adder = new AddThree();
		Long result = adder.apply(4L);
		System.out.println("result = " + result);
    }
}

不過現(xiàn)實中沒有這么用的,前面說過 Lambda 表達式是搭配函數(shù)式接口使用的,用Lambda表達式實現(xiàn)上Function 接口只需要一行,上面那個例子用 Lambda 實現(xiàn)的形式是:

Function<Long, Long> adder = (value) -> value + 3;Long resultLambda = adder.apply(8L);
System.out.println("resultLambda = " + resultLambda);

是不是簡潔了很多。后面的接口示例統(tǒng)一用 Lambda 表達式舉例,不再用類實現(xiàn)占用太多篇幅。

Function接口的常見應用是 Stream API 中的 map 操作方法,該方法的參數(shù)類型是Function接口,表示參數(shù)是一個“接收一個參數(shù),并返回一個值的函數(shù)”。

<R> Stream<R> map(Function<? super T, ? extends R> mapper);

所以我們在代碼里常會見到這樣使用 map 操作:

stream.map((value) -> value.toUpperCase())

Predicate

Predicate 接口 (全限定名:java.util.function.Predicate)表示一個接收單個參數(shù),并返回布爾值 true 或 false 的函數(shù)。以下是 Predicate 功能接口定義:

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

Predicate 接口里還有幾個提供了默認實現(xiàn)的方法,用于支持函數(shù)組合等功能,這里不再贅述。 用 Lambda 表達式實現(xiàn) Predicate 接口的形式如下:

Predicate predicate = (value) -> value != null;

Stream API 中的 filter 過濾操作,接收的就是一個實現(xiàn)了 Predicate 接口的參數(shù)。

Stream<T> filter(Predicate<? super T> predicate);

寫代碼時,會經(jīng)常見到這樣編寫的 filter 操作:

Stream<String> longStringsStream = stream.filter((value) -> {    
// 元素長度大于等于3,返回true,會被保留在 filter 產(chǎn)生的新流中。
    return value.length() >= 3;
});

Supplier

Supplier 接口(java.util.function.Supplier),表示提供某種值的函數(shù)。其定義如下:

@FunctionalInterfacepublic interface Supplier<T> {
    T get();
}

Supplier接口也可以被認為是工廠接口,它產(chǎn)生一個泛型結(jié)果。與 Function 不同的是,Supplier 不接受參數(shù)。

Supplier<Integer> supplier = () -> new Integer((int) (Math.random() * 1000D));

上面這個 Lambda 表達式的 Supplier 實現(xiàn),用于返回一個新的 Integer 實例,其隨機值介于 0 到 1000 之間。

Consume

Consumer 接口(java.util.function.Consume)表示一個函數(shù),該函數(shù)接收一個參數(shù),但是不返回任何值。

@FunctionalInterfacepublic interface Consumer<T> {    void accept(T t);
}

Consumer 接口常用于表示:要在一個輸入?yún)?shù)上執(zhí)行的操作,比如下面這個用Lambda 表達式實現(xiàn)的 Consumer,它將作為參數(shù)傳遞給它的value變量的值打印到System.out標準輸出中。

Consumer<Integer> consumer = (value) -> System.out.println(value);

Stream API 中的 forEach、peek 操作方法的參數(shù)就是 Consumer 接口類型的。

Stream<T> peek(Consumer<? super T> action);
void forEach(Consumer<? super T> action);

比如,Stream API 中的 forEach 操作,會像下面這樣使用 Consume 接口的實現(xiàn)

Stream<String> stream = stringList.stream();
// 下面是Lambda 的簡寫形式
// 完整形式為:value -> System.out.println(value);
stream.forEach(System.out::println);

Optional

Optional 接口并不是一個函數(shù)式接口,這里介紹它主要是因為它經(jīng)常在一些 Stream 操作中出現(xiàn),作為操作的返回值類型,所以趁著學習函數(shù)式編程的契機也學習一下它。

Optional 接口是預防NullPointerException的好工具,它是一個簡單的容器,其值可以是 null 或非 null。比如一個可能返回一個非空結(jié)果的方法,方法在有些情況下返回值,有些情況不滿足返回條件返回空值,這種情況下使用 Optional 接口作為返回類型,比直接無值時返回 Null 要更安全。 接下來我們看看 Optional 怎么使用:

// of 方法用于構(gòu)建一個 Optional 容器
Optional<String> optional = Optional.of("bam");
// 判斷值是否為空
optional.isPresent();           // true
// 取出值,如果不存在直接取會拋出異常
optional.get();                 // "bam"
// 取值,值為空時返回 orElse 提供的默認值
optional.orElse("fallback");    // "bam"
// 如果只存在,執(zhí)行ifPresent參數(shù)中指定的方法
optional.ifPresent((s) -> System.out.println(s.charAt(0)));// "b"

Stream 操作中像 findAny、 findFirst這樣的操作方法都會返回一個 Optional 容器,意味著結(jié)果 Stream 可能為空,因此沒有返回任何元素。我們可以通過 Optional 的 isPresent() 方法檢查是否找到了元素。

以上就是“Java函數(shù)式編程怎么應用”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注創(chuàng)新互聯(lián)行業(yè)資訊頻道。

當前標題:Java函數(shù)式編程怎么應用
分享路徑:http://jinyejixie.com/article22/ijjgcc.html

成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站建設、App開發(fā)、外貿(mào)建站網(wǎng)站營銷、網(wǎng)站改版、網(wǎng)站制作

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

商城網(wǎng)站建設
仁化县| 仁寿县| 江孜县| 南丰县| 元江| 建平县| 大渡口区| 沁水县| 萍乡市| 建湖县| 襄樊市| 怀集县| 双牌县| 沅江市| 襄城县| 昌图县| 富蕴县| 平江县| 博爱县| 宁晋县| 平武县| 贵溪市| 台南市| 衡阳市| 吐鲁番市| 邵阳市| 大港区| 阳春市| 锦州市| 长沙市| 台湾省| 阳新县| 葵青区| 开鲁县| 奉贤区| 中方县| 元朗区| 顺义区| 乌海市| 双江| 特克斯县|