Lambda與函數(shù)式接口

JDK8 于2014年發(fā)布至今咸产,已有6個年頭矢否。其中幾個重要的特性,lambda表達式脑溢,函數(shù)式接口僵朗,方法引用,默認方法等屑彻,至今未完全普及验庙。在回到家鄉(xiāng)邯鄲這個三線小城市后,竟然有同事問我社牲,有沒有簡單的統(tǒng)計List數(shù)據(jù)的方法粪薛,頗讓我驚訝。雖然我認為一個對公司有貢獻的程序員搏恤,首先應(yīng)該熟悉業(yè)務(wù)违寿。但是我也擔(dān)心,技術(shù)上落后太多熟空,也會拖累項目的性能藤巢。因此,本著服務(wù)家鄉(xiāng)的想法息罗,梳理一下Java程序員基本的知識點掂咒。與互聯(lián)網(wǎng)項目不同,我更加重視基礎(chǔ)知識。對于微服務(wù)框架绍刮,我認為絕大多數(shù)應(yīng)用糜工,是用不到的。如果為了使用而使用录淡,只會增加團隊不必要的負擔(dān)捌木。閑話不多說,我們開始介紹Lambda嫉戚,同時刨裆,會介紹函數(shù)式接口。

引言

Lambda表達式彬檀,也可以稱作閉包帆啃。它是推動Jdk8發(fā)布的最重要的特性。在此之前窍帝,jdk是不允許將函數(shù)作為參數(shù)進行傳遞的努潘,即不支持函數(shù)式編程。這使其在很多地方坤学,代碼過于啰嗦疯坤,如同老太太的叨叨。因此深浮,在Jdk8引入Lambda后压怠,代碼變得更加簡潔,更加清晰了飞苇。

Lambda語法

完整的Lambda表達式包含三部分組成:參數(shù)列表菌瘫,箭頭,聲明語句布卡。

(Type1 param1, Type2 param2, ..., TypeN paramN)-> {聲明1; 聲明2; ...... return mentmentM;}

以下是Lambda表達是的重要說明:

1雨让,參數(shù)可選類型聲明:不需要指明param1,param2的具體類型忿等,編譯器可以自動識別栖忠。于是,上面可以簡化為:

(param1, param2, ..., paramN)-> {指令1; 指令2; ......}

2这弧,參數(shù)可選圓括號:當參數(shù)僅有1個的時候娃闲,圓括號可以省略虚汛。于是匾浪,上面可以繼續(xù)簡化為:

param-> {指令1; 指令2; ......}

3,函數(shù)體可選大括號:當函數(shù)體中僅包含一個指令的時候卷哩,大括號可以省略蛋辈。

param-> 指令1;

具體表達式示例

// 1.不需要的參數(shù),返回變量5 
() -> 5

// 2.接收一個參數(shù)(數(shù)字類型),返回其2倍的值
x -> 2 * x

// 3.接受2個參數(shù)(數(shù)字)冷溶,并返回他們的差值
(x渐白,y) -> x-y

// 4.接收2個int型整數(shù),返回他們的和
(int x, int y) -> x + y

// 5.接受一個字符串對象逞频,并在控制臺打印纯衍,不返回任何值(看起來像是返回void)
(String s)-> System.out.print(s)

函數(shù)式接口

要想使用Lambda,必然需要一個函數(shù)式接口苗胀。那么襟诸,什么是函數(shù)式接口呢?函數(shù)式接口基协,F(xiàn)unctional Interface歌亲,必須有且僅有一個抽象方法的接口,對默認方法的個數(shù)沒有限制(Jdk8 允許在接口中有默認實現(xiàn))澜驮,同時函數(shù)式接口用@FunctionalInterface來標注陷揪。函數(shù)式接口可以友好的支持Lambda。

Jdk8 之前符合函數(shù)式接口條件的接口有:

  • java.lang.Runnable
  • java.util.concurrent.Callable
  • java.security.PrivilegedAction
  • java.util.Comparator
  • java.io.FileFilter
  • java.nio.file.PathMatcher
  • java.lang.reflect.InvocationHandler
  • java.beans.PropertyChangeListener
  • java.awt.event.ActionListener
  • javax.swing.event.ChangeListener

Jdk8 新增的函數(shù)接口一般都存儲在java.util.function中杂穷,其中有很多類悍缠,這里不一一贅述。感興趣的同學(xué)耐量,可以在這個package中自學(xué)一下扮休。接下來,我們舉兩個例子拴鸵,來說明函數(shù)式接口與Lambda的用法玷坠。

public class LambdaDemo {

    public static void main(String[] args) {
        runDemo();
    }

    public static void runDemo() {
        //傳統(tǒng)定義
        Runnable runnableJdk7 = new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread());
            }
        };
        runnableJdk7.run();

        //Lambda 形式
        Runnable runnableJdk8 = () -> System.out.println(Thread.currentThread());
        runnableJdk8.run();
    }

}

上述代碼,以定義一個線程為例劲藐,展示了Jdk8以前以及Jdk8的做法八堡。那么,Lambda究竟做了什么呢聘芜?很明顯兄渺,簡化了接口抽象方法的實現(xiàn),我們用Lambda實現(xiàn)了Runnablerun方法汰现,并創(chuàng)建了一個對象runnableJdk8挂谍。這就是函數(shù)式接口必須有且只能有一個抽象方法的原因。因為創(chuàng)建的時候瞎饲,等號右面必須有口叙,且只能有一個Lambda表達式,這個Lambda表達式嗅战,就是用來實現(xiàn)這個函數(shù)式接口唯一的抽象方法妄田。

我們再舉一個例子俺亮,假設(shè)有一張學(xué)生成績表,篩選成績大于80的疟呐。該怎么寫呢脚曾?

public static void score() {

        List<Student> listJdk7 = new ArrayList<>(
                Arrays.asList(
                        new Student(1, "張三", 41),
                        new Student(2, "王二", 92),
                        new Student(3, "李四", 52),
                        new Student(4, "王五", 81),
                        new Student(5, "趙六", 67)
                )
        );

        Iterator<Student> listIt = listJdk7.iterator();
        while (listIt.hasNext()) {
            Student next = listIt.next();
            if (next.getScore().intValue() < 80) {
                listIt.remove();
            }
        }
        System.out.println(JSON.toJSONString(listJdk7, SerializerFeature.PrettyFormat));
                
            //以下是Jdk8 Lambda 結(jié)合 函數(shù)式接口
        List<Student> listJdk8 = new ArrayList<>(
                Arrays.asList(
                        new Student(1, "張三", 41),
                        new Student(2, "王二", 92),
                        new Student(3, "李四", 52),
                        new Student(4, "王五", 81),
                        new Student(5, "趙六", 67)
                )
        );
        List<Student> collect = listJdk8.stream().filter(x -> x.getScore() > 80).collect(Collectors.toList());
        System.out.println(JSON.toJSONString(collect, SerializerFeature.PrettyFormat));
    }

Jdk7這里不再解釋,這里解釋一下Jdk8的實現(xiàn)启具。Jdk8調(diào)用了Stream的實現(xiàn)類java.util.stream.ReferencePipeline<P_IN, P_OUT>filter方法本讥,具體實現(xiàn)如下。

    @Override
    public final Stream<P_OUT> filter(Predicate<? super P_OUT> predicate) {
        Objects.requireNonNull(predicate);
        return new StatelessOp<P_OUT, P_OUT>(this, StreamShape.REFERENCE,
                                     StreamOpFlag.NOT_SIZED) {
            @Override
            Sink<P_OUT> opWrapSink(int flags, Sink<P_OUT> sink) {
                return new Sink.ChainedReference<P_OUT, P_OUT>(sink) {
                    @Override
                    public void begin(long size) {
                        downstream.begin(-1);
                    }

                    @Override
                    public void accept(P_OUT u) {
                        if (predicate.test(u))
                            downstream.accept(u);
                    }
                };
            }
        };
    }

從實現(xiàn)中鲁冯,我們可以看到囤踩,這個方法,傳入一個java.util.function.Predicate的接口函數(shù)晓褪,并且調(diào)用了其抽象方法test(T t)堵漱。該接口的內(nèi)容大致如下,其中抽象方法test涣仿,接受一個輸入對象勤庐,返回ture或者false。額好港,我覺得這有點廢話愉镰,畢竟代碼是這么寫的。(#.#)

@FunctionalInterface
public interface Predicate<T> {

    /**
     * Evaluates this predicate on the given argument.
     *
     * @param t the input argument
     * @return {@code true} if the input argument matches the predicate,
     * otherwise {@code false}
     */
    boolean test(T t);
        
        ...
}

我們剛才的寫法是

x -> x.getScore() > 80

x 代表我們要操作的對象钧汹,也就是test方法的入?yún)?code>t丈探。而x.getScore()>80則是test方法的具體實現(xiàn)。這個實現(xiàn)會返回boolean值拔莱。這樣是不是好理解了碗降。

總結(jié)

借助函數(shù)式接口與接口默認方法,可以使代碼變得更簡潔塘秦,更快速讼渊。

那么,要來一杯咖啡嗎尊剔?

附錄:Jdk8 自帶的函數(shù)式接口

序號 接口 & 描述
1 BiConsumer<T,U>代表了一個接受兩個輸入?yún)?shù)的操作爪幻,并且不返回任何結(jié)果
2 BiFunction<T,U,R>代表了一個接受兩個輸入?yún)?shù)的方法,并且返回一個結(jié)果
3 BinaryOperator<T>代表了一個作用于于兩個同類型操作符的操作须误,并且返回了操作符同類型的結(jié)果
4 BiPredicate<T,U>代表了一個兩個參數(shù)的boolean值方法
5 BooleanSupplier代表了boolean值結(jié)果的提供方
6 Consumer<T>代表了接受一個輸入?yún)?shù)并且無返回的操作
7 DoubleBinaryOperator代表了作用于兩個double值操作符的操作挨稿,并且返回了一個double值的結(jié)果。
8 DoubleConsumer代表一個接受double值參數(shù)的操作京痢,并且不返回結(jié)果奶甘。
9 DoubleFunction<R>代表接受一個double值參數(shù)的方法,并且返回結(jié)果
10 DoublePredicate代表一個擁有double值參數(shù)的boolean值方法
11 DoubleSupplier代表一個double值結(jié)構(gòu)的提供方
12 DoubleToIntFunction接受一個double類型輸入历造,返回一個int類型結(jié)果甩十。
13 DoubleToLongFunction接受一個double類型輸入船庇,返回一個long類型結(jié)果
14 DoubleUnaryOperator接受一個參數(shù)同為類型double,返回值類型也為double 吭产。
15 Function<T,R>接受一個輸入?yún)?shù)侣监,返回一個結(jié)果。
16 IntBinaryOperator接受兩個參數(shù)同為類型int,返回值類型也為int 臣淤。
17 IntConsumer接受一個int類型的輸入?yún)?shù)橄霉,無返回值 。
18 IntFunction<R>接受一個int類型輸入?yún)?shù)邑蒋,返回一個結(jié)果 姓蜂。
19 IntPredicate:接受一個int輸入?yún)?shù),返回一個布爾值的結(jié)果医吊。
20 IntSupplier無參數(shù)钱慢,返回一個int類型結(jié)果。
21 IntToDoubleFunction接受一個int類型輸入卿堂,返回一個double類型結(jié)果 束莫。
22 IntToLongFunction接受一個int類型輸入,返回一個long類型結(jié)果草描。
23 IntUnaryOperator接受一個參數(shù)同為類型int,返回值類型也為int 览绿。
24 LongBinaryOperator接受兩個參數(shù)同為類型long,返回值類型也為long。
25 LongConsumer接受一個long類型的輸入?yún)?shù)穗慕,無返回值饿敲。
26 LongFunction<R>接受一個long類型輸入?yún)?shù),返回一個結(jié)果逛绵。
27 LongPredicateR接受一個long輸入?yún)?shù)怀各,返回一個布爾值類型結(jié)果。
28 LongSupplier無參數(shù)术浪,返回一個結(jié)果long類型的值渠啤。
29 LongToDoubleFunction接受一個long類型輸入,返回一個double類型結(jié)果添吗。
30 LongToIntFunction接受一個long類型輸入沥曹,返回一個int類型結(jié)果。
31 LongUnaryOperator接受一個參數(shù)同為類型long,返回值類型也為long碟联。
32 ObjDoubleConsumer<T>接受一個object類型和一個double類型的輸入?yún)?shù)妓美,無返回值。
33 ObjIntConsumer<T>接受一個object類型和一個int類型的輸入?yún)?shù)鲤孵,無返回值壶栋。
34 ObjLongConsumer<T>接受一個object類型和一個long類型的輸入?yún)?shù),無返回值普监。
35 Predicate<T>接受一個輸入?yún)?shù)贵试,返回一個布爾值結(jié)果琉兜。
36 Supplier<T>無參數(shù),返回一個結(jié)果毙玻。
37 ToDoubleBiFunction<T,U>接受兩個輸入?yún)?shù)豌蟋,返回一個double類型結(jié)果
38 ToDoubleFunction<T>接受一個輸入?yún)?shù),返回一個double類型結(jié)果
39 ToIntBiFunction<T,U>接受兩個輸入?yún)?shù)桑滩,返回一個int類型結(jié)果梧疲。
40 ToIntFunction<T>接受一個輸入?yún)?shù),返回一個int類型結(jié)果运准。
41 ToLongBiFunction<T,U>接受兩個輸入?yún)?shù)幌氮,返回一個long類型結(jié)果。
42 ToLongFunction<T>接受一個輸入?yún)?shù)胁澳,返回一個long類型結(jié)果该互。
43 UnaryOperator<T>接受一個參數(shù)為類型T,返回值類型也為T。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末韭畸,一起剝皮案震驚了整個濱河市宇智,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌陆盘,老刑警劉巖普筹,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異隘马,居然都是意外死亡太防,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進店門酸员,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蜒车,“玉大人,你說我怎么就攤上這事幔嗦∧鹄ⅲ” “怎么了?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵邀泉,是天一觀的道長嬉挡。 經(jīng)常有香客問我,道長汇恤,這世上最難降的妖魔是什么庞钢? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮因谎,結(jié)果婚禮上基括,老公的妹妹穿的比我還像新娘。我一直安慰自己财岔,他們只是感情好风皿,可當我...
    茶點故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布河爹。 她就那樣靜靜地躺著,像睡著了一般桐款。 火紅的嫁衣襯著肌膚如雪咸这。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天鲁僚,我揣著相機與錄音炊苫,去河邊找鬼裁厅。 笑死冰沙,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的执虹。 我是一名探鬼主播拓挥,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼袋励!你這毒婦竟也來了侥啤?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤茬故,失蹤者是張志新(化名)和其女友劉穎盖灸,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體磺芭,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡赁炎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了钾腺。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片徙垫。...
    茶點故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖放棒,靈堂內(nèi)的尸體忽然破棺而出姻报,到底是詐尸還是另有隱情,我是刑警寧澤间螟,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布吴旋,位于F島的核電站,受9級特大地震影響厢破,放射性物質(zhì)發(fā)生泄漏荣瑟。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一溉奕、第九天 我趴在偏房一處隱蔽的房頂上張望褂傀。 院中可真熱鬧,春花似錦加勤、人聲如沸仙辟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽叠国。三九已至未檩,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間粟焊,已是汗流浹背冤狡。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留项棠,地道東北人悲雳。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像香追,于是被迫代替她去往敵國和親合瓢。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,446評論 2 348

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