【java8新特性】lambda表達(dá)式與函數(shù)式接口詳解

歡迎交流java8新特性系列文章:http://www.reibang.com/nb/27231419 . [1][2][3][4][5]

一拳魁、簡介

???? java8于2014年發(fā)布疾瓮,相比于java7,java8新增了非常多的特性饲做,如lambda表達(dá)式线婚、函數(shù)式接口、方法引用盆均、默認(rèn)方法塞弊、新工具(編譯工具)、Stream API泪姨、Date Time API游沿、Optional等 。 當(dāng)前很多公司的老產(chǎn)品依然使用的java7肮砾,甚至開發(fā)人員開發(fā)新產(chǎn)品時依然沒有選擇升級诀黍, 寫關(guān)于java8系列文章的目的在于梳理和分享java8新增的主要特性,開發(fā)時也可以用作參考唇敞。

???? lambda表達(dá)式是java8新增的主要特性之一蔗草,lambda表達(dá)式又稱閉包或匿名函數(shù)咒彤,主要優(yōu)點在于簡化代碼疆柔、增強(qiáng)代碼可讀性咒精、并行操作集合等。至于是否使用旷档,有的同學(xué)覺得不適應(yīng)模叙,有的同學(xué)欲罷不能,見仁見智~

????技多不壓身鞋屈,本文將采用由淺入深的方式范咨,講解java8 lambda表達(dá)式的語法及使用,并附帶代碼進(jìn)行演示厂庇。

二渠啊、lambda語法

lambda的基本語法:

 (parameters) -> expression
 or
 (parameters) ->{ statements; }

lambda表達(dá)式的特性:

  1. 可選類型聲明: 無需聲明參數(shù)類型,編譯器即可自動識別
  2. 可選的參數(shù)圓括號: 僅有一個參數(shù)時圓括號可以省略
  3. 可選的大括號:主體只包含一個語句時可省略大括號
  4. 可選的返回關(guān)鍵字:主體只包含一個表達(dá)式返回值并省略大括號時权旷,編譯器會自動return返回值替蛉;有大括號時,需要顯式指定表達(dá)式return了一個數(shù)值

特性示例:

//1拄氯、無參數(shù)躲查,返回值1
() -> 1 
//2、無參數(shù)译柏,無返回值
() -> System.out.print("Java8 lambda.");
//3镣煮、1個參數(shù),參數(shù)類型為數(shù)字鄙麦,返回值為其值的5倍
x ->  5 * x 
//4典唇、2個參數(shù),參數(shù)類型均為數(shù)字胯府,返回值為其差值
(x, y) -> x - y
//5介衔、2個參數(shù),指定參數(shù)類型均為int型盟劫,返回值為其差值 
(int x, int y) -> x - y  
//6夜牡、1個參數(shù),指定參數(shù)類型為String 侣签,無返回值
(String str) -> System.out.print(str)

個人建議塘装,從程序的嚴(yán)謹(jǐn)性角度出發(fā),盡量指明函數(shù)的參數(shù)類型影所,避免出錯1碾取!猴娩!

三阴幌、java8 lambda使用示例

前面我們講到lambda表達(dá)式的語法和特性勺阐,那么在java8中如何使用lambda表達(dá)式呢?我們先以用幾個示例來展現(xiàn)lambda表達(dá)式在java8中的使用矛双。

3.1?? java Runnable接口的lambda實現(xiàn)

????用lambdah代替匿名類是java8中l(wèi)ambda的常用形式渊抽,本文以開發(fā)同學(xué)經(jīng)常使用的Runnable接口匿名類為示例,演示如何用lambda表達(dá)式來代替匿名類:

  1. 在java8之前:
   new Thread(new Runnable()
   {
    @Override
    public void run()
    {
         System.out.println("No use lambda.");
    }
   }).start();
  1. 在java8之后:
   new Thread(() -> System.out.println("Use lambda")).start();

????可以看到议忽,java8中利用lambda表達(dá)式大大簡化了代碼編寫懒闷。
????此處簡要提下,用lambda表達(dá)式代替匿名類的關(guān)鍵在于栈幸,匿名類實現(xiàn)的接口使用了java.lang.FunctionalInterface注解愤估,且只有一個待實現(xiàn)的抽象接口方法,如Runnable接口:

  @FunctionalInterface
  public interface Runnable {
      public abstract void run();
  }

本文后面會詳細(xì)講解FunctionalInterface注解速址。

3.2?? java List迭代的lambda實現(xiàn)

????開發(fā)同學(xué)經(jīng)常會使用到集合類玩焰,并對集合類對象進(jìn)行迭代,以實現(xiàn)業(yè)務(wù)邏輯芍锚。
????java8中昔园,集合類的頂層接口java.lang.Iterable定義了一個forEach方法:

    /* @param action The action to be performed for each element
     * @throws NullPointerException if the specified action is null
     * @since 1.8
     */
    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }

forEach方法可以迭代集合的所有對象,其參數(shù)為Consumer對象闹炉,Consumer類位于java.util.function包下蒿赢,我們看下其定義:

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

到此已經(jīng)很容易聯(lián)想到,我們可以采用lambda表達(dá)式來實現(xiàn)java8集合的迭代邏輯渣触,下面我們進(jìn)行示例:

  1. 在java8之前:
    List<Integer> features = Arrays.asList(1,2);
    for (Integer feature : features) {
         System.out.println(feature);
     }
  1. 在java8之后:
    List<Integer> features = Arrays.asList(1,2);
    features.forEach(n -> System.out.println(n));

上述邏輯還可以用java8的方法引用來表示:

    List<Integer> features = Arrays.asList(1,2);
    features.forEach(System.out::println);

方法引用也是java8的新特性羡棵,由::操作符標(biāo)示,詳細(xì)可參考方法引用的文章嗅钻,本文不贅述皂冰。

四、函數(shù)式接口

????在上一節(jié)中我們提到:"用lambda表達(dá)式代替匿名類的關(guān)鍵在于养篓,匿名類實現(xiàn)的接口使用了java.lang.FunctionalInterface注解秃流,且只有一個待實現(xiàn)的抽象接口方法", 這里的接口便是函數(shù)式接口。
????函數(shù)式接口(Functional Interface)是java8新增的特性柳弄,它是一個有且僅有一個抽象方法舶胀,但是可以有多個非抽象方法的接口。函數(shù)式接口可以被隱式轉(zhuǎn)換為lambda表達(dá)式碧注。
????Runnable接口是在JDK1.8之前已經(jīng)存在的接口嚣伐,在JDK1.8中加入了@FunctionalInterface注解,表示將其定義為一個函數(shù)式接口萍丐。在JDK1.8中定義的函數(shù)式接口還有:

  • 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

JDK1.8新增加的函數(shù)式接口有java.util.function包下的接口轩端,典型的如上一節(jié)中提到的Consumer接口,感興趣的讀者可以閱讀JDK1.8的源碼逝变,在此不逐個列出基茵,在下一節(jié)本文還會列舉java.util.function包中典型的函數(shù)式接口的使用奋构。

到這里,可以總結(jié)出拱层,java8中用lambda表達(dá)式代替匿名內(nèi)部類弥臼,本質(zhì)上是將接口定義為函數(shù)式接口,并將函數(shù)式接口隱式轉(zhuǎn)換為lambda表達(dá)式舱呻、

五醋火、典型函數(shù)式接口的使用

????上一節(jié)我們理解了java8函數(shù)式接口的概念和定義方法,本節(jié)再列舉java.util.function幾個典型的函數(shù)式接口的使用,加深下函數(shù)式接口與lambda表達(dá)式結(jié)合的理解色解。

5.1??Predicate接口
5.1.1??Predicate接口的基本用法

Predicate接口適合用于過濾信卡,測試對象是否符合某個條件,Predicate接口源碼如下:

@FunctionalInterface
public interface Predicate<T> {

    boolean test(T t);
    
    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }
   
    default Predicate<T> negate() {
        return (t) -> !test(t);
    }

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

可以看到瘩欺,Predicate接口待實現(xiàn)的唯一抽象方法是 boolean test(T t) 方法。我們用Predicate接口實現(xiàn)從整數(shù)型數(shù)組中過濾正數(shù):

    public static void main(String[] args)
    {  
        List<Integer> numbers = Arrays.asList(-1, -2, 0, 4, 5);
        filter(numbers, n -> n > 0);
    }
    
    public static void filter(List<Integer> numbers, Predicate<Integer> condition)
    {
        for (Integer number : numbers)
        {
            if (condition.test(number))
            {
                System.out.println("Eligible number: " + number);
            }
        }
    }

運(yùn)行結(jié)果如下:

Eligible number: 4
Eligible number: 5

對數(shù)組的迭代,還可以使用Stream API的方式:

    public static void main(String[] args)
    {
        List<Integer> numbers = Arrays.asList(-1, -2, 0, 4, 5);
        numbers.stream().filter(n -> n > 0).forEach(n -> System.out.println("Eligible number: " + n));
    }

上面的代碼采用Stream API + Predicate接口 + Consumer接口的方式實現(xiàn)了同樣的功能怎栽,代碼量大大減少。Stream API(java.util.stream)同樣是java8的新特性宿饱,將真正的函數(shù)式編程風(fēng)格引入到j(luò)ava語言中熏瞄,進(jìn)一步簡化了代碼。

5.1.2??Predicate接口的進(jìn)階用法

????我們再看上一節(jié)提到的Predicate接口的源碼谬以,發(fā)現(xiàn)它有三個default關(guān)鍵字定義的方法强饮,分別為and()、negate()为黎、or()三個方法邮丰,顧名思義,它們類似于邏輯操作&&铭乾、!剪廉、||,用于生成新的Predicate對象炕檩。
????以上一節(jié)的數(shù)組為例斗蒋,我們用and操作過濾出數(shù)組中大于-1且小于5的數(shù)字:

    public static void main(String[] args)
    {  
        List<Integer> numbers = Arrays.asList(-1, -2, 0, 4, 5);
        filter(numbers, n -> n > -1 , n -> n < 5);
    }
    
    public static void filter(List<Integer> numbers, Predicate<Integer> first, Predicate<Integer> second)
    {
        for (Integer number : numbers)
        {
            if (first.and(second).test(number))
            {
                System.out.println("Eligible number: " + number);
            }
        }
    }

結(jié)果為:

Eligible number: 0
Eligible number: 4

????上例用and()方法將兩個Predicate對象進(jìn)行and運(yùn)算,同理negate()笛质、or()方法的使用也很簡單泉沾,在此不再贅述。

5.2 ??Stream API

????Stream API將處理的數(shù)據(jù)源看做一種Stream(流)经瓷,Stream(流)在Pipeline(管道)中傳輸和運(yùn)算爆哑,Stream API結(jié)合lambda表達(dá)式可以很方便的對集合進(jìn)行篩選、排序等運(yùn)算舆吮,由于篇幅較大揭朝,請閱讀【java8新特性】Stream API詳解队贱,本文中不詳細(xì)講解。

5.3 ??Optional

????Optional類是Java8為了解決null值判斷問題潭袱,借鑒google guava類庫的Optional類而引入的一個同名Optional類柱嫌,使用Optional類配合lambda表達(dá)式可以避免顯式的null值判斷,并實現(xiàn)很多類似Stream API的功能屯换,由于篇幅較大编丘,請閱讀【java8新特性】Optional詳解,本文中不詳細(xì)講解彤悔。

五嘉抓、注意事項

  1. lambda表達(dá)式可以使用方法引用,當(dāng)且僅當(dāng)主體中不修改lambda表達(dá)式提供的參數(shù)晕窑,如第三章提到的兩種寫法
features.forEach(n -> System.out.println(n));
等價于
features.forEach(System.out::println);

而如果對參數(shù)有任何修改時不能使用方法引用抑片,如:

features.forEach(n -> System.out.println(n+1));
  1. lambda與匿名類的聯(lián)系和區(qū)別
    聯(lián)系:
     1) 都可以訪問final或effectively final局部變量。
     2) 生成的對象都可以調(diào)用實現(xiàn)的接口方法杨赤。
    區(qū)別:
     1) this指針的指向不同敞斋。我們知道匿名類的this指針指向匿名類,而lambda表達(dá)式的this指針指向的是包圍lambda表達(dá)式的類疾牲。
     2) 編譯方式不同植捎。lambda在編譯器內(nèi)部被翻譯為私有方法,并使用了Java 7的 invokedynamic 字節(jié)碼指令來動態(tài)綁定這個方法
     3) 實現(xiàn)的接口限制有區(qū)別阳柔。匿名類可以為任意接口創(chuàng)建實例焰枢,只要實現(xiàn)接口所有的抽象方法即可;而lambda表達(dá)式只能實現(xiàn)函數(shù)式接口(只有一個必須實現(xiàn)的抽象方法)盔沫。
     4) 接口默認(rèn)方法的調(diào)用權(quán)限不同医咨。匿名類實現(xiàn)的抽象方法允許調(diào)用接口中的默認(rèn)方法,而lambda表達(dá)式不能調(diào)用接口中的默認(rèn)方法架诞。

  1. 【java8新特性】lambda表達(dá)式與函數(shù)式接口詳解 ?

  2. 【java8新特性】Stream API詳解 ?

  3. 【java8新特性】Optional詳解 ?

  4. 【java8新特性】方法引用 ?

  5. 【java8新特性】默認(rèn)方法 ?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末拟淮,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子谴忧,更是在濱河造成了極大的恐慌很泊,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,427評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件沾谓,死亡現(xiàn)場離奇詭異委造,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)均驶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評論 3 395
  • 文/潘曉璐 我一進(jìn)店門昏兆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人妇穴,你說我怎么就攤上這事爬虱×フ” “怎么了?”我有些...
    開封第一講書人閱讀 165,747評論 0 356
  • 文/不壞的土叔 我叫張陵跑筝,是天一觀的道長死讹。 經(jīng)常有香客問我,道長曲梗,這世上最難降的妖魔是什么赞警? 我笑而不...
    開封第一講書人閱讀 58,939評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮虏两,結(jié)果婚禮上愧旦,老公的妹妹穿的比我還像新娘。我一直安慰自己碘举,他們只是感情好忘瓦,可當(dāng)我...
    茶點故事閱讀 67,955評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著引颈,像睡著了一般。 火紅的嫁衣襯著肌膚如雪境蜕。 梳的紋絲不亂的頭發(fā)上蝙场,一...
    開封第一講書人閱讀 51,737評論 1 305
  • 那天,我揣著相機(jī)與錄音粱年,去河邊找鬼售滤。 笑死,一個胖子當(dāng)著我的面吹牛台诗,可吹牛的內(nèi)容都是我干的完箩。 我是一名探鬼主播,決...
    沈念sama閱讀 40,448評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼拉队,長吁一口氣:“原來是場噩夢啊……” “哼弊知!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起粱快,我...
    開封第一講書人閱讀 39,352評論 0 276
  • 序言:老撾萬榮一對情侶失蹤秩彤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后事哭,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體漫雷,經(jīng)...
    沈念sama閱讀 45,834評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,992評論 3 338
  • 正文 我和宋清朗相戀三年鳍咱,在試婚紗的時候發(fā)現(xiàn)自己被綠了降盹。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,133評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡谤辜,死狀恐怖蓄坏,靈堂內(nèi)的尸體忽然破棺而出仅胞,到底是詐尸還是另有隱情,我是刑警寧澤剑辫,帶...
    沈念sama閱讀 35,815評論 5 346
  • 正文 年R本政府宣布干旧,位于F島的核電站,受9級特大地震影響妹蔽,放射性物質(zhì)發(fā)生泄漏椎眯。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,477評論 3 331
  • 文/蒙蒙 一胳岂、第九天 我趴在偏房一處隱蔽的房頂上張望编整。 院中可真熱鬧,春花似錦乳丰、人聲如沸掌测。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽汞斧。三九已至,卻和暖如春什燕,著一層夾襖步出監(jiān)牢的瞬間粘勒,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評論 1 272
  • 我被黑心中介騙來泰國打工屎即, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留庙睡,地道東北人。 一個月前我還...
    沈念sama閱讀 48,398評論 3 373
  • 正文 我出身青樓技俐,卻偏偏與公主長得像乘陪,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子雕擂,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,077評論 2 355

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