中文字幕精品亚洲无线码二区,国产黄a三级三级三级看三级,亚洲七七久久桃花影院,丰满少妇被猛烈进入,国产小视频在线观看网站

Java8函數(shu)之旅(四(si)) --四(si)大函數(shu)接口

前言

??Java8中函數接(jie)口(kou)有(you)很(hen)(hen)多(duo),大概有(you)幾十個(ge)吧,具體究(jiu)竟(jing)是多(duo)少(shao)我也(ye)數不清(qing),所以一(yi)開始看的(de)時候感覺一(yi)臉懵逼(bi),不過(guo)其實根本沒(mei)那么(me)復雜,畢竟(jing)不應該也(ye)沒(mei)必要(yao)把一(yi)個(ge)東西設計的(de)很(hen)(hen)復雜。

幾個單詞

??在學習了解之前,希望大家能記住幾個單詞,掌握這幾個單詞,什么3,40個官方的函數接口都是小問題了,不信的話接著往下看啦。ok,那這幾個單詞呢分別是supplier 提供者,consumer 消費者,function 函數,operation 運算符,binary 二元(就是數學里二元一次方程那個二元,代表2個的意思),雙重的

四大基礎函數接口

??函數接口,你可以理解為對一段行為的抽象,簡單點說可以在方法就是將一段行為作為參數進行傳遞,這個行為呢,可以是一段代碼,也可以是一個方法,那你可以想象在java8之前要將一段方法作為參數傳遞只能通過匿名內部類來實現,而且代碼很難看,也很長,函數接口就是對匿名內部類的優化。
??雖(sui)然類(lei)(lei)庫中的基本函數(shu)(shu)接口特別多,但其(qi)實總體可以分成四類(lei)(lei),就(jiu)好像阿拉(la)伯數(shu)(shu)字是(shi)無(wu)限多的,但總共就(jiu)10個(ge)基本數(shu)(shu)字一樣,理解了這4個(ge),其(qi)他(ta)的就(jiu)都明白了。

Functio<t,r>接口

?? function,顧名思義,函數的意思,這里的函數是指數學上的函數哦,你也可以說是嚴格函數語言中的函數,例如haskell里的,他接受一個參數,返回一個值,永遠都是這樣,是一個恒定的,狀態不可改變的方法。其實想講函數這個徹底將明白可以再開一篇博客了,所以這里不詳細的說了。
?? 上(shang)面(mian)說到,函數接口是對行(xing)為(wei)的(de)抽象(xiang),因此我方(fang)便大家理解,就用java中(zhong)的(de)方(fang)法作例(li)子(zi)。

?? Fcuntion接口是(shi)對接受一個T類型參數,返(fan)回R類型的(de)結果的(de)方(fang)(fang)法的(de)抽象,通過調用(yong)apply方(fang)(fang)法執(zhi)行內容。

public class Operation{

/* 
    下面(mian)這個(ge)(ge)方法(fa)接(jie)受(shou)一個(ge)(ge)int類型(xing)參數(shu)a,返回(hui)(hui)a+1,符合我上面(mian)說(shuo)的接(jie)受(shou)一個(ge)(ge)參數(shu),返回(hui)(hui)一個(ge)(ge)值(zhi)
    所(suo)以呢(ni)(ni)這個(ge)(ge)方法(fa)就符合Function接(jie)口的定義,那(nei)要怎么用呢(ni)(ni),繼續(xu)看(kan)例子 
*/
public static final int addOne(int a){
    return a+1;
}

/* 
    該方(fang)法第二個參數接受一(yi)個function類(lei)型的(de)行為,然后(hou)調用apply,對a執行這(zhe)段行為
*/
public static int oper(int a, Function<Integer,Integer> action){
    return action.apply(a);
}

/* 下面(mian)調(diao)用這個oper方(fang)法,將addOne方(fang)法作(zuo)為參數傳遞 */
pulic static void main(String[] args){
    int x = 1;
    int y = oper(x,x -> addOne(x));//這里(li)可以換成方法(fa)引用的寫(xie)法(fa) int y = oper(x,Operation::addOne)
    System.out.printf("x= %d, y = %d", x, y); // 打印結果 x=1, y=2
    
    /* 當然你也(ye)可以使用lambda表(biao)達式來表(biao)示這段行為,只(zhi)要保證(zheng)一個參數(shu),一個返回值(zhi)就能匹配(pei) */
     y = oper(x, x -> x + 3 ); // y = 4
     y = oper(x, x -> x * 3 ); // y = 3    
}

}

 

這里的箭頭指向的位置就是形(xing)參,可以看到第二個(ge)箭頭的Lambda表(biao)達式指向了Funtion接口

Consumer 接口

Consumer 接口翻譯過來就是消費者,顧名思義,該接口對應的方法類型為接收一個參數,沒有返回值,可以通俗的理解成將這個參數'消費掉了',一般來說使用Consumer接口往往伴隨著一些期望狀態的改變或者事件的發生,例如最典型的forEach就是使用的Consumer接口,雖然沒有任何的返回值,但是卻向控制臺輸出了語句。
Consumer 使用accept對參數執(zhi)行行為

    public static void main(String[] args) {
        Consumer<String> printString = s -> System.out.println(s);
        printString.accept("helloWorld!");
        //控制臺輸出 helloWorld!
    }

Supplier 接口

Supplier 接口翻譯過(guo)來就是提供(gong)者,和上面(mian)的消費(fei)者相反(fan),該(gai)接口對應的方法(fa)類(lei)型為不接受參數,但是提供(gong)一個(ge)返回(hui)(hui)(hui)值,通俗(su)的理解為這(zhe)種接口是無私的奉獻(xian)者,不僅(jin)不要參數,還返回(hui)(hui)(hui)一個(ge)值,使用(yong)get()方法(fa)獲得這(zhe)個(ge)返回(hui)(hui)(hui)值

        Supplier<String> getInstance = () -> "HelloWorld!";
        System.out.println(getInstance.get());
        // 控偶(ou)值臺(tai)輸出 HelloWorld

Predicate 接口

predicate<t,boolean> 謂(wei)語接(jie)口,顧(gu)名思義,中(zhong)文(wen)中(zhong)的(de)‘是’與‘不(bu)是’是中(zhong)文(wen)語法的(de)謂(wei)語,同樣的(de)該接(jie)口對應的(de)方(fang)法為(wei)接(jie)收一個參數,返回一個Boolean類型值,多(duo)用(yong)于判斷與過濾,當然你可(ke)以(yi)把(ba)他理解成特殊的(de)Funcation<t,r>,但是為(wei)了便于區分語義,還是單(dan)獨的(de)劃了一個接(jie)口,使用(yong)test()方(fang)法執行(xing)這段(duan)行(xing)為(wei)

 public static void main(String[] args) {
     Predicate<Integer> predOdd = integer -> integer % 2 == 1;
     System.out.println(predOdd.test(5));
     //控制臺輸出 5
 }

其他的接口

介(jie)紹完正面這(zhe)四種最基本的接(jie)口,剩余的接(jie)口就可(ke)以很容(rong)易(yi)的理解了(le),java8中定義(yi)了(le)幾十種的函數接(jie)口,但是剩下的接(jie)口都(dou)是上(shang)面這(zhe)幾種接(jie)口的變種,大多為(wei)限制參數類型,數量,下面舉幾個(ge)例子(zi)。

類型限制接口

  • 參數類型,例如IntPredicate,LongPredicate, DoublePredicate,這幾個接口,都是在基于Predicate接口的,不同的就是他們的泛型類型分別變成了Integer,Long,Double,IntConsumer,LongConsumer, DoubleConsumer比如這幾個,對應的就是Consumer<Integer>,Consumer<Long>,Consumer<Double>,其余的是一樣的道理,就不再舉例子了
  • 返回值類型,和上面類似,只是命名的規則上多了一個To,例如IntToDoubleFunction,IntToLongFunction, 很明顯就是對應的Funtion<Integer,Double> 與Fcuntion<Integer,Long>,其余同理,另外需要注意的是,參數限制與返回值限制的命名唯一不同就是To,簡單來說,前面不帶To的都是參數類型限制,帶To的是返回值類型限制,對于沒有參數的函數接口,那顯而易見只可能是對返回值作限制。例如LongFunction<R>就相當于Function<Long,R> 而多了一個To的ToLongFunction<T>就相當于Function<T,Long>,也就是對返回值類型作了限制。

數量限制接口

  • 有些接口需要接受兩名參數,此類接口的所有名字前面都是附加上Bi,是Binary的縮寫,開頭也介紹過這個單詞了,是二元的意思,例如BiPredicate,BiFcuntion等等,而由于java沒有多返回值的設定,所以Bi指的都是參數為兩個

Operator接口

  • 此類接口只有2個分別是UnaryOperator<T> 一元操作符接口,與BinaryOperator<T>二元操作符接口,這類接口屬于Function接口的簡寫,他們只有一個泛型參數,意思是Funtion的參數與返回值類型相同,一般多用于操作計算,計算 a + b的BiFcuntion如果限制條件為Integer的話 往往要這么寫BiFunction<Integer,Integer,Integer> 前2個泛型代表參數,最后一個代表返回值,看起來似乎是有點繁重了,這個時候就可以用BinaryOperator<Integer>來代替了。

下面是各種類型的接口的示意圖,相信只要真正理解了,其實問題并不大

關于lambda的限制

Java8中的(de)lambda表達式(shi),并不是完(wan)全閉(bi)包,lambda表達式(shi)對值封(feng)閉(bi),不對變量封(feng)閉(bi)。簡單點來說就是局部變量在lambda表達式(shi)中如(ru)果(guo)要使(shi)用,必須是聲明final類(lei)型或(huo)者是隱(yin)式(shi)的(de)final例如(ru)

int num = 123;
Consumer<Integer> print = () -> System.out.println(num);

就是(shi)可以的(de)(de),雖然(ran)num沒有被聲明為final,但(dan)從整體來看,他和final類(lei)型的(de)(de)變量(liang)的(de)(de)表(biao)現是(shi)一致的(de)(de),可如果是(shi)這樣的(de)(de)代碼

int num = 123;
num ++;
Consumer<Integer> print = () -> System.out.println(num);

則無法通過編譯器,這就是對值封閉(也就是棧上的變量封閉)
如果上文中的num是實例變量或者是靜態變量就沒有這個限制。
看到這(zhe)(zhe)里(li),自然而然就會(hui)有(you)疑問為什(shen)么(me)會(hui)這(zhe)(zhe)樣?或者(zhe)說(shuo)為什(shen)么(me)要這(zhe)(zhe)么(me)設計(ji)。理由(you)有(you)很多(duo),例如函數的不變性,線程安全等(deng)(deng)等(deng)(deng)等(deng)(deng),這(zhe)(zhe)里(li)我給一(yi)個(ge)簡單的說(shuo)明

  • 為什么局部變量會有限制而靜態變量和全局變量就沒有限制,因為局部變量是保存在棧上的,而眾所周知,棧上的變量都隱式的表現了它們僅限于它們所在的線程,而靜態變量與實例變量是保存在靜態區與堆中的,而這兩塊區域是線程共享的,所以訪問并沒有問題。
  • 現在我們假設如果lambda表達式可以局部變量的情況,實例變量存儲在堆中,局部變量存儲在棧上,而lambda表達式是在另外一個線程中使用的,那么在訪問局部變量的時候,因為線程不共享,因此lambda可能會在分配該變量的線程將這個變量收回之后,去訪問該變量。所以說,Java在訪問自由局部變量時,實際上是在訪問它的副本,而不是訪問原始變量。如果局部變量僅僅賦值一次那就沒有什么區別了。
  • 嚴格保證這種限制會讓你的代碼變得無比安全,如果你學習或了解過一些經典的函數式語言的話,就會知道不變性的重要性,這也是為什么stream流可以十分方便的改成并行流的重要原因之一。

總結

本篇(pian)介(jie)紹了四大函數接(jie)口和(he)他們引(yin)申出的各(ge)類接(jie)口,終點是對不(bu)同種類行(xing)為的封裝導致(zhi)了設計出不(bu)同的函數接(jie)口,另(ling)外在使用函數接(jie)口或(huo)者lambda表達式的時候,要注意lambda對值封閉這個(ge)特性(xing)。

posted on 2018-03-23 09:45  little fat  閱讀(1108)  評論(0)    收藏  舉報