<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Java8新特性探索之函數(shù)式接口

          共 3156字,需瀏覽 7分鐘

           ·

          2020-11-02 05:05

          點擊上方藍色字體,選擇“標星公眾號”

          優(yōu)質文章,第一時間送達

          ? 作者?|??拼搏吧,少年

          來源 |? urlify.cn/vQry6r

          66套java從入門到精通實戰(zhàn)課程分享

          一、為什么引入函數(shù)式接口

          作為Java函數(shù)式編程愛好者,我們都知道方法引用和 Lambda 表達式都必須被賦值,同時賦值需要類型信息才能使編譯器保證類型的正確性。

          我們先看一個Lambda代碼示例:

          x?->?x.toString()

          我們清楚這里返回類型必須是?String,但?x?是什么類型呢?

          Lambda 表達式包含類型推導(編譯器會自動推導出類型信息,避免了程序員顯式地聲明),編譯器必須能夠以某種方式推導出?x?的類型以生成正確的代碼。

          同樣方法引用也存在此問題,假設你要傳遞?System.out :: println?到你正在編寫的方法 ,你怎么知道傳遞給方法的參數(shù)的類型?

          為了解決上述問題,Java 8 引入了函數(shù)式接口,在?java.util.function?包,它包含一組接口,這些接口是 Lambda 表達式和方法引用的目標類型,每個接口只包含一個抽象方法,稱為函數(shù)式方法。只有確保接口中有且僅有一個抽象方法,Lambda表達式的類型信息才能順利地進行推導。

          二、如何使用函數(shù)式接口

          在編寫接口時,可以使用?@FunctionalInterface?注解強制執(zhí)行此函數(shù)式方法模式:

          1. 在接口上使用注解?@FunctionalInterface?,一旦使用該注解來定義接口,編譯器將會強制檢查該接口是否確實有且僅有一個抽象方法,否則將會報錯。


            @FunctionalInterface
            public?interface?MyFunction?{
            ?/**
            ??*?自定義的抽象方法
            ??*/
            ?void?run();
            }
          2. 在函數(shù)式接口,有且僅有一個抽象方法,Objectpublic方法除外


            @FunctionalInterface
            public?interface?MyFunction?{
            ?
            ?/**
            ??*?自定義的抽象方法
            ??*/
            ?void?run();
            ?
            ?/**
            ??*?Object的equals方法
            ??*?@param?obj
            ??*?@return
            ??*/
            ?@Override
            ?boolean?equals(Object?obj);
            ?
            ?/**
            ??*?Object的toString方法
            ??*?@return
            ??*/
            ?@Override
            ?String?toString();
            ?
            ?/**
            ??*?Object的hashCode方法
            ??*?@return
            ??*/
            ?@Override
            ?int?hashCode();
            ?
            }
          3. 在函數(shù)式接口中,我們可以使用default修飾符定義默認方法,使用static修飾符定義靜態(tài)方法


            @FunctionalInterface
            public?interface?MyFunction?{
            ?
            ?/**
            ??*?自定義的抽象方法
            ??*/
            ?void?run();
            ?
            ?/**
            ??*?static修飾符定義靜態(tài)方法
            ??*/
            ????static?void?staticRun()?{
            ????????System.out.println("接口中的靜態(tài)方法");
            ????}
            ?
            ????/**
            ?????*?default修飾符定義默認方法
            ?????*/
            ????default?void?defaultRun()?{
            ????????System.out.println("接口中的默認方法");
            ????}
            ????
            }
          • 為大家演示下自定義無泛型的函數(shù)式接口測試實例:


            /**
            *?自定義的無泛型函數(shù)式接口
            */
            @FunctionalInterface
            public?interface?MyFunction?{
            ?
            ?/**
            ??*?自定義的抽象方法
            ??*?@param?x
            ??*/
            ?void?run(Integer?x);
            ?
            ????/**
            ?????*?default修飾符定義默認方法
            ?????*?@param?x
            ?????*/
            ????default?void?defaultMethod(Integer?x)?{
            ????????System.out.println("接口中的默認方法,接收參數(shù)是:"?+?x);
            ????}
            ????
            }

            /**
            *?測試類
            */
            public?class?MyFunctionTest?{

            ?@Test
            ?public?void?functionTest()?{
            ??test(6,?(x)?->?System.out.println("接口中的抽象run方法,接收參數(shù)是:"?+?x));
            ?}
            ?
            ?public?void?test(int?n,?MyFunction?function)?{
            ??System.out.println(n);
            ??function.defaultMethod(n);
            ??function.run(n);
            ?}
            ?
            }

            輸出結果:

            6
            接口中的默認方法,接收參數(shù)是:6
            接口中的抽象run方法,接收參數(shù)是:6
          • 為大家演示下自定義有泛型的函數(shù)式接口測試實例:


            /**
            ?*?自定義的有泛型函數(shù)式接口
            ?*/
            @FunctionalInterface
            public?interface?MyFunctionGeneric?{

            ?/**
            ??*?轉換值
            ??*?@param?t
            ??*?@return
            ??*/
            ?T?convertValue(T?t);
            ?
            }

            /**
            *?測試類
            */
            public?class?MyFunctionGenericTest?{

            ?@Test
            ?public?void?convertValueTest()?{
            ??String?result?=?toLowerCase((x)?->?x.toLowerCase(),?"ABC");
            ??System.out.println(result);
            ?}
            ?
            ?public?String?toLowerCase(MyFunctionGeneric?functionGeneric,?String?value)?{
            ??return?functionGeneric.convertValue(value);
            ?}
            ?
            }
            輸出結果:
            abc

            注意:作為參數(shù)傳遞 Lambda 表達式:為了將 Lambda 表達式作為參數(shù)傳遞,接收Lambda 表達式的參數(shù)類型必須是與該 Lambda 表達式兼容的函數(shù)式接口 的類型。

          三、Java8四大內置核心函數(shù)式接口

          首先總覽下四大函數(shù)式接口的特點說明:

          接口參數(shù)類型返回類型方法說明
          ConsumerTvoidvoid accept(T t)消費型接口,對類型T參數(shù)操作,無返回結果
          Supplier-TT get()供給型接口,創(chuàng)造T類型參數(shù)
          FunctionTRR apply(T t)函數(shù)型接口,對類型T參數(shù)操作,返回R類型參數(shù)
          PredicateTbooleanboolean test(T t)斷言型接口,對類型T進行條件篩選操作

          消費型接口Consumer

          java.util.function.Consumer?接口是消費一個數(shù)據(jù),其數(shù)據(jù)類型由泛型決定。

          接口源碼:

          package?java.util.function;

          import?java.util.Objects;

          @FunctionalInterface
          public?interface?Consumer?{

          ????void?accept(T?t);

          ????default?Consumer?andThen(Consumer?after)?{
          ????????Objects.requireNonNull(after);
          ????????return?(T?t)?->?{?accept(t);?after.accept(t);?};
          ????}
          }
          1. 抽象方法:void accept(T t),接收并消費一個指定泛型的數(shù)據(jù),無需返回結果。
          2. 默認方法:default Consumer andThen(Consumer after),如果一個方法的參數(shù)和返回值全都是 Consumer 類型,那么就可以實現(xiàn)效果:消費數(shù)據(jù)的時候,首先做一個操作,然后再做一個操作,實現(xiàn)組合
          public?class?ConsumerTest?{

          ?/**
          ??*?先計算總分,再計算平均分
          ??*/
          ?@Test
          ?public?void?calculate()?{
          ??Integer[]?fraction?=?new?Integer[]?{?65,?76,?85,?92,?88,?99?};
          ??consumer(fraction,?x?->?System.out.println(Arrays.stream(x).mapToInt(Integer::intValue).sum()),
          ????y?->?System.out.println(Arrays.stream(y).mapToInt(Integer::intValue).average().getAsDouble()));
          ?}
          ?
          ?public?void?consumer(Integer[]?fraction,?Consumer?x,?Consumer?y)?{
          ??x.andThen(y).accept(fraction);
          ?}
          ?
          }

          輸出結果:

          505
          84.16666666666667

          由于Consumerdefault方法所帶來的嵌套調用(連鎖調用),對行為的抽象的函數(shù)式編程理念,展示的淋漓盡致。

          其他的消費型函數(shù)式接口匯總說明:

          接口名稱方法名稱方法簽名
          DoubleConsumeraccept(double) -> void
          IntConsumeraccept(int) -> void
          LongConsumeraccept(long) -> void
          ObjDoubleConsumeraccept(T, double) -> void
          ObjIntConsumeraccept(T, int) -> void
          ObjLongConsumeraccept(T, long) -> void

          供給型接口Supplier

          java.util.function.Supplier?接口僅包含一個無參的方法:?T get()?,用來獲取一個泛型參數(shù)指定類型的對象數(shù)據(jù)。

          接口源碼:

          package?java.util.function;

          @FunctionalInterface
          public?interface?Supplier?{
          ????T?get();
          }

          由于這是一個函數(shù)式接口,意味著對應的Lambda表達式需要對外提供一個符合泛型類型的對象數(shù)據(jù)。

          public?class?SupplierTest?{

          ?public?int?getMax(Supplier?supplier)?{
          ??return?supplier.get();
          ?}
          ?
          ?/**
          ??*?獲取數(shù)組元素最大值
          ??*/
          ?@Test
          ?public?void?getMaxTest()?{
          ??Integer[]?data?=?new?Integer[]?{?5,?4,?6,?3,?2,?1?};
          ??int?result?=?getMax(()?->?{
          ???int?max?=?0;
          ???for?(int?i?=?0;?i?????max?=?Math.max(max,?data[i]);
          ???}
          ???return?max;
          ??});
          ??System.out.println(result);
          ?}
          ?
          }

          其他的供給型函數(shù)式接口匯總說明:

          接口名稱方法名稱方法簽名
          BooleanSuppliergetAsBoolean() -> boolean
          DoubleSuppliergetAsDouble() -> double
          IntSuppliergetAsInt() -> int
          LongSuppliergetAsLong() -> long

          函數(shù)型接口Function

          java.util.function.Function?接口用來根據(jù)一個類型的數(shù)據(jù)得到另一個類型的數(shù)據(jù),前者稱為前置條件,后者稱為后置條件。

          接口源碼:

          package?java.util.function;

          import?java.util.Objects;

          @FunctionalInterface
          public?interface?Function?{

          ????R?apply(T?t);

          ????default??Function?compose(Function?before)?{
          ????????Objects.requireNonNull(before);
          ????????return?(V?v)?->?apply(before.apply(v));
          ????}

          ????default??Function?andThen(Function?after)?{
          ????????Objects.requireNonNull(after);
          ????????return?(T?t)?->?after.apply(apply(t));
          ????}

          ????static??Function?identity()?{
          ????????return?t?->?t;
          ????}
          }
          1. 抽象方法?apply(T t):該方法接收入?yún)⑹且粋€泛型T對象,并返回一個泛型T對象。

            默認方法

            andThen(Function after):該方法接受一個行為,并將父方法處理過的結果作為參數(shù)再處理。

            compose(Function before):該方法正好與andThen相反,它是先自己處理然后將結果作為參數(shù)傳給父方法執(zhí)行。


            @Test
            public?void?andThenAndComposeTest()?{
            ????//?計算公式相同
            ????Function?andThen1?=?x?->?x?+?1;
            ????Function?andThen2?=?x?->?x?*?2;
            ????Function?compose1?=?y?->?y?+?1;
            ????Function?compose2?=?y?->?y?*?2;
            ????//?注意調用的先后順序
            ????//?傳入?yún)?shù)2后,先執(zhí)行andThen1計算,將結果再傳入andThen2計算
            ????System.out.println(andThen1.andThen(andThen2).apply(2));
            ????//?傳入?yún)?shù)2后,先執(zhí)行compose2計算,將結果再傳入compose1計算
            ????System.out.println(compose1.compose(compose2).apply(2));
            }
            輸出結果:
            6
            5

          2. 靜態(tài)方法identity():獲取到一個輸入?yún)?shù)和返回結果一樣的Function實例。

          來一個自駕九寨溝的代碼示例:

          public?class?FunctionTest?{
          ?
          ?@Test
          ?public?void?findByFunctionTest()?{
          ??Function?getMoney?=?m?->?m.add(new?BigDecimal(1000));
          ??BigDecimal?totalCost?=?getMoney.apply(new?BigDecimal(500));
          ??System.out.println("張三的錢包原本只有500元,自駕川西得去銀行再取1000元,取錢后張三錢包總共有"?+???????????Function.identity().apply(totalCost)?+?"元");
          ??BigDecimal?surplus?=?cost(totalCost,?(m)?->?{
          ???System.out.println("第二天出發(fā)前發(fā)現(xiàn)油不足,加油前有"?+?m?+?"元");
          ???BigDecimal?lubricate?=?m.subtract(new?BigDecimal(300));
          ???System.out.println("加油300后還剩余"?+?lubricate?+?"元");
          ???return?lubricate;
          ??},?(m)?->?{
          ???System.out.println("到達景區(qū)門口,買景區(qū)票前有"?+?m?+?"元");
          ???BigDecimal?tickets?=?m.subtract(new?BigDecimal(290));
          ???System.out.println("買景區(qū)票290后還剩余"?+?tickets?+?"元");
          ???return?tickets;
          ??});
          ??System.out.println("最后張三返程到家還剩余"?+?surplus?+?"元");
          ?}

          ?public?BigDecimal?cost(BigDecimal?money,?Function?lubricateCost,
          ???Function?ticketsCost)?{
          ??Function?firstNight?=?(m)?->?{
          ???System.out.println("第一晚在成都住宿前有"?+?m?+?"元");
          ???BigDecimal?first?=?m.subtract(new?BigDecimal(200));
          ???System.out.println("交完200住宿費還剩余"?+?first?+?"元");
          ???return?first;
          ??};
          ??Function?secondNight?=?(m)?->?{
          ???System.out.println("第二晚在九寨縣住宿前有"?+?m?+?"元");
          ???BigDecimal?second?=?m.subtract(new?BigDecimal(200));
          ???System.out.println("交完200住宿費還剩余"?+?second?+?"元");
          ???return?second;
          ??};
          ??return?lubricateCost.andThen(ticketsCost).andThen(secondNight).compose(firstNight).apply(money);
          ?}

          }

          輸出結果:

          張三的錢包原本只有500元,自駕川西得去銀行再取1000元,取錢后張三錢包總共有1500元
          第一晚在成都住宿前有1500元
          交完200住宿費還剩余1300元
          第二天出發(fā)前發(fā)現(xiàn)油不足,加油前有1300元
          加油300后還剩余1000元
          到達景區(qū)門口,買景區(qū)票前有1000元
          買景區(qū)票290后還剩余710元
          第二晚在九寨縣住宿前有710元
          交完200住宿費還剩余510元
          最后張三返程到家還剩余510元

          其他的函數(shù)型函數(shù)式接口匯總說明:

          接口名稱方法名稱方法簽名
          BiFunctionapply(T, U) -> R
          DoubleFunctionapply(double) -> R
          DoubleToIntFunctionapplyAsInt(double) -> int
          DoubleToLongFunctionapplyAsLong(double) -> long
          IntFunctionapply(int) -> R
          IntToDoubleFunctionapplyAsDouble(int) -> double
          IntToLongFunctionapplyAsLong(int) -> long
          LongFunctionapply(long) -> R
          LongToDoubleFunctionapplyAsDouble(long) -> double
          LongToIntFunctionapplyAsInt(long) -> int
          ToDoubleFunctionapplyAsDouble(T) -> double
          ToDoubleBiFunctionapplyAsDouble(T, U) -> double
          ToIntFunctionapplyAsInt(T) -> int
          ToIntBiFunctionapplyAsInt(T, U) -> int
          ToLongFunctionapplyAsLong(T) -> long
          ToLongBiFunctionapplyAsLong(T, U) -> long

          斷言型接口Predicate

          java.util.function.Predicate?接口中包含一個抽象方法:?boolean test(T t)?,用于條件判斷的場景。默認方法:and or nagte?(取反)。

          接口源碼:

          package?java.util.function;

          import?java.util.Objects;

          @FunctionalInterface
          public?interface?Predicate?{

          ????boolean?test(T?t);

          ????default?Predicate?and(Predicate?other)?{
          ????????Objects.requireNonNull(other);
          ????????return?(t)?->?test(t)?&&?other.test(t);
          ????}

          ????default?Predicate?negate()?{
          ????????return?(t)?->?!test(t);
          ????}

          ????default?Predicate?or(Predicate?other)?{
          ????????Objects.requireNonNull(other);
          ????????return?(t)?->?test(t)?||?other.test(t);
          ????}

          ????static??Predicate?isEqual(Object?targetRef)?{
          ????????return?(null?==?targetRef)
          ??????????????????Objects::isNull
          ????????????????:?object?->?targetRef.equals(object);
          ????}
          }

          既然是條件判斷,就會存在與、或、非三種常見的邏輯關系。其中將兩個?Predicate?條件使用邏輯連接起來實現(xiàn)并且的效果時,類始于?Consumer接口?andThen()函數(shù) 其他三個雷同。

          public?class?PredicateTest?{
          ?/**
          ??*?查找在渝北的Jack
          ??*/
          ?@Test
          ?public?void?findByPredicateTest()?{
          ??List?list?=?Lists.newArrayList(new?User("Johnson",?"渝北"),?new?User("Tom",?"渝中"),?new?User("Jack",?"渝北"));
          ??getNameAndAddress(list,?(x)?->?x.getAddress().equals("渝北"),?(x)?->?x.getName().equals("Jack"));
          ?}
          ?
          ?public?void?getNameAndAddress(List?users,?Predicate?name,?Predicate?address)?{
          ??users.stream().filter(user?->?name.and(address).test(user)).forEach(user?->?System.out.println(user.toString()));
          ?}
          }

          輸出結果:

          User?[name=Jack,?address=渝北]

          其他的斷言型函數(shù)式接口匯總說明:

          接口名稱方法名稱方法簽名
          BiPredicatetest(T, U) -> boolean
          DoublePredicatetest(double) -> boolean
          IntPredicatetest(int) -> boolean
          LongPredicatetest(long) -> boolean

          四、總結

          Lambda 表達式和方法引用并沒有將 Java 轉換成函數(shù)式語言,而是提供了對函數(shù)式編程的支持。這對 Java 來說是一個巨大的改進,因為這允許你編寫更簡潔明了,易于理解的代碼。




          粉絲福利:實戰(zhàn)springboot+CAS單點登錄系統(tǒng)視頻教程免費領取

          ???

          ?長按上方微信二維碼?2 秒
          即可獲取資料



          感謝點贊支持下哈?

          瀏覽 16
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  波多野结衣视频在线观看 | 18禁黄无码 | 韩国三级电影久久久 | 日本亲与子乱人妻hd | 狠狠色狠狠撸 |