函數(shù)式接口

概述

java8中新增了 @FunctionalInterface 注解表示函數(shù)式接口掸掸,用注解@FunctionalInterface標識的接口都是函數(shù)式接口杉畜,函數(shù)式接口只能有一個抽象方法纪蜒,但除了抽象方法,java8還允許接口中定義默認方法(聲明為default的方法)和靜態(tài)方法此叠。如果一個接口只有一個抽象方法纯续,即便我們并沒有用 @FunctionalInterface 注解標注随珠,編譯器依然會將該接口看做是函數(shù)式接口。如果我們自己定義的接口只有一個抽象方法猬错,加不加 @FunctionalInterface 注解都可以窗看,如果加了的話,編譯器就會做函數(shù)式接口檢查倦炒,不滿足函數(shù)式接口的要求就會報出相應(yīng)的異常显沈,編譯通不過的。

但是有一點特殊情況逢唤,就是函數(shù)式接口中如果重寫了Object類中聲明為public的方法拉讯,那么編譯器并不認為這是一個抽象方法。因為java中所有的類都是Object的子類鳖藕,我們函數(shù)式接口中重寫了的Object類中聲明為public的方法可以被實現(xiàn)函數(shù)式接口的類直接從Object類繼承魔慷,所以編譯器并不把重寫了Object類中聲明為public的方法當做抽象方法。

@FunctionalInterface
public interface MyInterface {
   void hello();
   String toString();  //重寫了Object類中的toString方法
}

四大函數(shù)式接口

java8中內(nèi)置了四大函數(shù)式接口著恩,通過這四大函數(shù)式接口我們可以完成大多數(shù)的需求院尔,如果這四大函數(shù)接口不能滿足需求,java中還擴展了其他的函數(shù)式接口喉誊,并且也可以自己定義函數(shù)式接口邀摆。

1.Consumer接口

Consumer是一個消費者接口,accpet(T t)方法給定一個參數(shù)伍茄,把它消費了栋盹,不返回任何結(jié)果。Consumer函數(shù)式接口的源碼如下:

@FunctionalInterface
public interface Consumer<T> {

   void accept(T t);
   
   default Consumer<T> andThen(Consumer<? super T> after) {
       Objects.requireNonNull(after);
       return (T t) -> { accept(t); after.accept(t); };
   }
}

示例1:

Consumer<Integer> consumer = (i) -> System.out.println(i);
       consumer.accept(666);

示例2:

public class Test01 {
   public static void main(String[] args) {
       List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9);
       list.forEach(i -> System.out.println(i));
   }
}

forEach()方法源碼跟蹤:

forEach()方法位于Iterable<T>接口中幻林,參數(shù)是一個Consumer接口類型贞盯。

default void forEach(Consumer<? super T> action) {
   Objects.requireNonNull(action);
   for (T t : this) {
       action.accept(t);
   }
}

List<>接口繼承了Collection<E>接口,而Collection<E>接口又繼承了Iterable<E>接口沪饺,所以List可以使用forEach()方法遍歷元素躏敢。

public interface List<E> extends Collection<E> {...}
public interface Collection<E> extends Iterable<E> {...}

2.Function接口

Function接口中的抽象方法為 apply(T t),該方法有一個參數(shù),并返回結(jié)果整葡。除了apply這個抽象方法件余,還有其他的默認方法和靜態(tài)方法,源碼如下:

@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;
   }
}

示例1:

public class FunctionTest {

   public static void main(String[] args) {
       System.out.println(compute(2,i -> i * 3));
       System.out.println(compute(2,i -> i*i));
       System.out.println(compute(2,i -> i + 3));
   }

   private static int compute(int a,Function<Integer,Integer> function){
       int result = function.apply(a);  
       return result;
   }
}
執(zhí)行結(jié)果

compose和andThen方法分析:

compose方法是將兩個Function組合在一起遭居,先調(diào)用參數(shù)里給定的Function的apply方法:before.apply(v)啼器,再將參數(shù)里的Function的結(jié)果給當前的Function(也就是作為返回值的Function)的apply方法作參數(shù):apply(before.apply(v)),將結(jié)果返回俱萍。

andThen方法也是將兩個Function組合在一起端壳,但是andThen方法正好和compose方法相反,是先調(diào)用當前的Function的apply方法:apply(t)枪蘑,再將當前的Function的結(jié)果給參數(shù)里的Function的apply方法作參數(shù):after.apply(apply(t))损谦,將結(jié)果返回岖免。

示例2

public class FunctionTest {

   public static void main(String[] args) {

       System.out.println(compute1(2,i -> i * 3,i -> i * i));
       System.out.println(compute2(2,i -> i * 3,i -> i * i));
   }

   private static int compute1(int a,Function<Integer,Integer> function1,Function<Integer,Integer> function2){
       return function1.compose(function2).apply(a);
   }

   private static int compute2(int a,Function<Integer,Integer> function1,Function<Integer,Integer> function2){
       return function1.andThen(function2).apply(a);
   }
}
執(zhí)行結(jié)果

補充:以上的Function函數(shù)式接口只能傳入一個參數(shù),并返回結(jié)果照捡,如果要傳入兩個參數(shù)并返回執(zhí)行結(jié)果颅湘,F(xiàn)unction函數(shù)式接口是做不到的,那么這時可以使用通過Function接口擴展的BiFunction函數(shù)式接口栗精,該接口有三個泛型T闯参,U,R悲立,前兩個作為方法的輸入?yún)?shù)類型鹿寨,最后一個作為返回結(jié)果類型。

該接口源碼如下:

@FunctionalInterface
public interface BiFunction<T, U, R> {

   R apply(T t, U u);

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

示例3:

public class FunctionTest {

   public static void main(String[] args) {
       
       System.out.println(compute3(2,3,(i,j) -> i + j));
       System.out.println(compute3(2,3,(i,j) -> i * j));
   }

   private static int compute3(int a, int b, BiFunction<Integer,Integer,Integer> biFunction){
       return biFunction.apply(a,b);
   }
}
執(zhí)行結(jié)果

通過BiFunction接口的源碼级历,我們可以知道释移,該接口有andThen這個默認方法叭披,沒有compose方法寥殖,那么為什么BiFunction接口沒有提供compose方法呢?其實這點也是很好理解的涩蜘,假設(shè)BiFunction接口可以提供compose方法,并假設(shè)定義如下:

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

依照上面我們對Funciton接口的compose方法的分析嚼贡,先調(diào)用參數(shù)里給定的BiFunction的apply方法:before.apply(v,u),再將參數(shù)里的BiFunction的結(jié)果給當前的BiFunction的apply方法作參數(shù)同诫,將結(jié)果返回粤策。

因為BiFunction的apply方法有兩個參數(shù),而Function和BiFunction接口的apply方法都只有一個返回值误窖,所以不論參數(shù)里給定的是Funciton類型叮盘,還是BiFunction類型,都是不滿足要求的霹俺。

下面分析BiFunction的andThen方法:

BiFunction的andThen方法的返回值類型是BiFunction,參數(shù)類型是Function,這和上面的Function的andThen方法是不一樣的柔吼,那么為什么BiFunction的andThen方法的的參是Function類型,而不是BiFunction類型呢丙唧?其實這點也不能理解愈魏。

andThen方法是先調(diào)用當前的BiFunction的apply方法:apply(t, u),再將當前的BiFunction的結(jié)果給參數(shù)里的Function的apply方法作參數(shù),又因為 R apply(T t, U u)想际,BiFunction的apply方法的返回值只有一個培漏,所以參數(shù)里只能是Function類型,如果參數(shù)里也是BiFunction類型胡本,那么需要BiFunction的apply方法有兩個參數(shù)牌柄。

示例4:

public class FunctionTest {

   public static void main(String[] args) {
       System.out.println(compute4(2,3,(i,j) -> i + j,i -> i * i));
   }
   
   private static int compute4(int a,int b,BiFunction<Integer,Integer,Integer> biFunction,
                           Function<Integer,Integer> function){
       return biFunction.andThen(function).apply(a,b);
   }
}
執(zhí)行結(jié)果

3.Predicate接口

Predicate是一個斷言接口,給定一個參數(shù)侧甫,返回一個boolean值珊佣,即給定一個條件返回true或者false傻昙。其源碼如下:

@FunctionalInterface
public interface Predicate<T> {
   boolean test(T t);
    //返回兩個Predicate的邏輯與的結(jié)果
   default Predicate<T> and(Predicate<? super T> other) {
       Objects.requireNonNull(other);
       return (t) -> test(t) && other.test(t);
   }
    //返回predicate取反后的結(jié)果
   default Predicate<T> negate() {
       return (t) -> !test(t);
   }
    //返回兩個predicate邏輯或后的結(jié)果
   default Predicate<T> or(Predicate<? super T> other) {
       Objects.requireNonNull(other);
       return (t) -> test(t) || other.test(t);
   }

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

示例1:

public class PredicateTest {

    public static void main(String[] args) {
        Predicate<Integer> predicate = i -> i > 5;
        System.out.println(predicate.test(4));
        System.out.println(predicate.test(6));
    }
}
執(zhí)行結(jié)果

示例2:

public class PredicateTest {

    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
        conditionFilter(list,i -> i % 2 == 0);
    }

    private static void conditionFilter(List<Integer> list,Predicate<Integer> predicate){
        for (Integer integer : list) {
            if(predicate.test(integer)){
                System.out.println(integer);
            }
        }
    }
}
執(zhí)行結(jié)果

示例3(and方法示例):

public class PredicateTest {

   public static void main(String[] args) {
       List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
       //找出集合list中大于5并且是偶數(shù)的數(shù)
       conditionFilter1(list,i -> i > 5, i -> i % 2 == 0);
   }

   private static void conditionFilter1(List<Integer> list,Predicate<Integer> predicate1,Predicate<Integer> predicate2){
       for (Integer integer : list) {
           if(predicate1.and(predicate2).test(integer)){
               System.out.println(integer);
           }
       }
   }
}
執(zhí)行結(jié)果

示例4(or方法示例):

public class PredicateTest {

   public static void main(String[] args) {
       List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
       //找出集合list中大于5或者是偶數(shù)的數(shù)
       conditionFilter2(list,i -> i > 5,i -> i % 2 == 0 );
   }

   private static void conditionFilter2(List<Integer> list,Predicate<Integer> predicate1,Predicate<Integer> predicate2){
       for (Integer integer : list) {
           if(predicate1.or(predicate2).test(integer)){
               System.out.println(integer);
           }
       }
   }
}
執(zhí)行結(jié)果

示例5(negate方法示例):

public class PredicateTest {

    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
        //找出集合list中的所有奇數(shù)
        conditionFilter3(list,i -> i % 2 == 0);
    }

    private static void conditionFilter3(List<Integer> list,Predicate<Integer> predicate){
        for (Integer integer : list) {
            if(predicate.negate().test(integer)){
                System.out.println(integer);
            }
        }
    }
}

執(zhí)行結(jié)果

4.Supplier接口

Supplier是一個提供者接口,不接受任何參數(shù)彩扔,同時返回一個結(jié)果妆档,其源碼如下:

@FunctionalInterface
public interface Supplier<T> {

    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}

示例:

public class Teacher {
    private String tName = "zhangkai";

    public String gettName() {
        return tName;
    }

    public void settName(String tName) {
        this.tName = tName;
    }
}
public class SupplierTest {

    public static void main(String[] args) {
        Supplier<Teacher> supplier = () -> new Teacher();
        System.out.println(supplier.get().gettName());
    }
}

總結(jié)

寫到這里總算結(jié)束了,可能篇幅有點長虫碉,看起來心情有點不爽贾惦,當我看別人的播客文章的時候,太長了也會感覺很無聊敦捧,不想繼續(xù)看下去须板,自己寫的時候也長篇大論了,寫的篇幅長的主要原因是扣的太細了兢卵,接口里的其他默認方法也分析了一下习瑰,而那些默認方法可能實際中用到的比較少,甚至幾乎就用不到秽荤,這么做也主要是想對函數(shù)式接口了解更多一點甜奄,不至于只知道函數(shù)式接口中常用的抽象方法,也是為了我個人以后哪天看看自己寫的播客復(fù)習一下窃款,人的遺忘真的是太快了课兄,尤其是計算機行業(yè),要學(xué)好多東西晨继,雖學(xué)的多烟阐,但如果沒有輸出,沒有筆記的話紊扬,真的過段時間就一點都沒了蜒茄,忘得很干凈,提起來只有一個印象餐屎,具體的用法以及細節(jié)已經(jīng)想不起來了檀葛,這時自己的播客文章或筆記看一遍,就又都拾起來了啤挎。如果讀者覺得沒必要看那么細驻谆,也許我寫的還有些復(fù)雜,有些廢話連篇庆聘,那么不常用的那些默認方法可以不看胜臊,直接跳過即可。當然我也不能面面具到伙判,由著四大函數(shù)式接口可以擴展出其他很多的函數(shù)式接口象对,比如出入兩個參數(shù)的BiConsumer,BiPredicate等等,用到的時候大家可以查下jdk的api文檔接口宴抚。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末勒魔,一起剝皮案震驚了整個濱河市甫煞,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌冠绢,老刑警劉巖抚吠,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異弟胀,居然都是意外死亡楷力,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進店門孵户,熙熙樓的掌柜王于貴愁眉苦臉地迎上來萧朝,“玉大人,你說我怎么就攤上這事夏哭〖旒恚” “怎么了?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵竖配,是天一觀的道長何址。 經(jīng)常有香客問我,道長械念,這世上最難降的妖魔是什么头朱? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮龄减,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘班眯。我一直安慰自己希停,他們只是感情好,可當我...
    茶點故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布署隘。 她就那樣靜靜地躺著宠能,像睡著了一般。 火紅的嫁衣襯著肌膚如雪磁餐。 梳的紋絲不亂的頭發(fā)上违崇,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天,我揣著相機與錄音诊霹,去河邊找鬼羞延。 笑死,一個胖子當著我的面吹牛脾还,可吹牛的內(nèi)容都是我干的伴箩。 我是一名探鬼主播,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼鄙漏,長吁一口氣:“原來是場噩夢啊……” “哼嗤谚!你這毒婦竟也來了棺蛛?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤巩步,失蹤者是張志新(化名)和其女友劉穎旁赊,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體椅野,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡彤恶,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了鳄橘。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片声离。...
    茶點故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖瘫怜,靈堂內(nèi)的尸體忽然破棺而出术徊,到底是詐尸還是另有隱情,我是刑警寧澤鲸湃,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布赠涮,位于F島的核電站,受9級特大地震影響暗挑,放射性物質(zhì)發(fā)生泄漏笋除。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一炸裆、第九天 我趴在偏房一處隱蔽的房頂上張望垃它。 院中可真熱鬧,春花似錦烹看、人聲如沸国拇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽酱吝。三九已至,卻和暖如春土思,著一層夾襖步出監(jiān)牢的瞬間务热,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工己儒, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留崎岂,地道東北人。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓址愿,卻偏偏與公主長得像该镣,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,914評論 2 355

推薦閱讀更多精彩內(nèi)容