Java 8 Lambda表達式入門

1.1 為什么使用Lambda表達式

先看幾個例子:

第一個例子年扩,在一個獨立的線程中執(zhí)行某項任務待侵,我們通常這么實現(xiàn):

class Worker implements Runnable {
    public void run() {
        for (int i = 0; i < 100; i++)
            doWork();
    }
    ...
}

Worker w = new Worker();
new Thread(w).start();

第二個例子胶滋,自定義字符串比較的方法(通過字符串長度)瘾腰,一般這么做:

class LengthComparator implements Comparator<String> {
    public int compare(String first, String second) {
        return Integer.compare(first.length(), second.length());
    }
}
Arrays.sort(strings, new LengthComparator());

第三個例子皆的,在JavaFX中,給一個button添加一個callback:

button.setOnAction(new EventHandler<ActionEvent>() {
    public void handle(ActionEvent event) {
        System.out.println("Thanks for clicking!");
    }
});

這些例子有一個共同點蹋盆,就是:先定義一段代碼塊祭务,傳給某個對象或方法,然后被執(zhí)行怪嫌。在Lambda表達式之前义锥,Java是不允許直接傳遞代碼塊的,因為Java是面向?qū)ο蟮难颐穑虼吮仨殏鬟f一個對象拌倍,將要執(zhí)行的代碼塊封裝到對象里。

1.2 Lambda表達式的語法

將上面第二個例子中的LengthComparator噪径,用Lambda表達式表示為:

(String first, String second) -> Integer.compare(first.length(), second.length());

->前為參數(shù)列表柱恤,其后為表達式語句體;

如果表達式語句體不止一行找爱,則將語句體寫在{}中梗顺,與普通的函數(shù)一樣:

(String first, String second) -> {
    if (first.length() > second.length()) {
        return 1;
    } else if (first.length() == second.length()) {
        return 0;
    } else {
        return -1;
    }
};

如果沒有參數(shù),()還是需要帶上车摄,比如上面的第一個例子寺谤,可以表示為:

() -> {
    for (int i = 0; i < 1000; i ++) {
        doWork();
    }
}

如果參數(shù)的類型可以從上下文自動推斷,則可以省略:

Comparator<String> comp
    = (first, second) // Same as (String first, String second)
    -> Integer.compare(first.length(), second.length());

如果參數(shù)只有一個吮播,且類型可以自動推斷变屁,則小括號()也可以省略:

// Instead of (event) -> or (ActionEvent event) ->
eventHandler<ActionEvent> listener = event -> System.out.println("Thanks for clicking!");

lambda表達式的返回值的類型是自動推斷的,因此不需要指明意狠;在lambda表達式粟关,某些條件分支中有返回值,而其它分支沒有返回值环戈,是不允許的闷板,如:

(x) -> {
    if (x >= 0) {
        return 1;
    }
}

另外,expression lambdastatement lambda的區(qū)別是院塞,expression lambda不需要寫return關(guān)鍵字遮晚,Java runtime會將表達式的結(jié)果作為返回值返回,而statement lambda是寫在{}中的表達式迫悠,需要使用return關(guān)鍵字鹏漆,比如:

// expression lambda
Comparator<String> comp1 = (first, second) -> Integer.compare(first.length(), second.length());

// statement lambda
Comparator<String> comp2 = (first, second) -> { return Integer.compare(first.length(), second.length());};

1.3 Functional Interface

如果一個接口(interface)僅有一個抽象方法(abstract method)巩梢,就稱為Functional Interface创泄,比如Runnable艺玲、Comparator等。
在任何需要一個Functional Interface對象的地方鞠抑,都可以使用lambda表達式:

Arrays.sort(words, (first, second) -> Integer.compare(first.length(), second.length()));

這里饭聚,sort()的第二個參數(shù)需要的是一個Comparator對象,而ComparatorFunctional Interface搁拙,因此可以直接傳入lambda表達式秒梳,在調(diào)用該對象的compare()方法時,就是執(zhí)行該lambda表達式中的語句體箕速;

如果lambda表達式的語句體會拋出異常酪碘,則對應的Functional Interface中的抽象方法必須拋出了該異常,否則就需要在lambda表達式中顯式捕獲異常:

Runnable r = () -> {
   System.out.println("------");
   try {
       Thread.sleep(10);
   } catch (InterruptedException e) {
       // catch exception
   }
};

Callable<String> c = () -> {
    System.out.println("--------");
    Thread.sleep(10);
    return "";
};

1.4 Method Reference

如果將lambda表達式的參數(shù)作為參數(shù)傳遞給一個方法盐茎,他們的執(zhí)行效果是相同的兴垦,則該lambda表達式可以使用Method Reference表達,以下兩種方式是等價的:

(x) -> System.out.println(x)
System.out::println

其中System.out::println被稱為Method Reference字柠。

Method Reference主要有三種形式:

object::instanceMethod
Class::staticMethod
Class::instanceMethod

對于前兩種方式探越,對應的lambda表達式的參數(shù)和method的參數(shù)是一致的,比如:

System.out::println
(x) -> System.out.println(x)

Math::pow  
(x, y) -> Math.pow(x, y)

對于第三種方式窑业,對應的lambda表達式的語句體中钦幔,第一個參數(shù)作為對象,調(diào)用method常柄,將其它參數(shù)作為method的參數(shù)鲤氢,比如:

String::compareToIgnoreCase
(s1, s2) -> s1.compareToIgnoreCase(s2)

1.5 Constructor Reference

Constructor ReferenceMethod Reference類似,只不過是特殊的method:new西潘,具體調(diào)用的是哪個構(gòu)造函數(shù)铜异,由上下文環(huán)境決定,比如:

List<String> labels = ...;
Stream<Button> stream = labels.stream().map(Button::new);

Button::new等價于(x) -> Button(x)秸架,所以調(diào)用的構(gòu)造函數(shù)是:Button(x);

除了創(chuàng)建單個對象揍庄,也可以創(chuàng)建對象數(shù)組,如下面兩種方式等價:

int[]::new  
(x) -> new int[x]

1.6 變量作用域

lambd表達式會捕獲當前作用域下可用的變量东抹,比如:

public void repeatMessage(String text, int count) {
    Runnable r = () -> {
        for (int i = 0; i < count; i ++) {
            System.out.println(text);
            Thread.yield();
        }
    };
    new Thread(r).start();
}

但是這些變量必須是不可變的蚂子,為什么呢?看下面這個例子:

int matches = 0;
for (Path p : files)
    new Thread(() -> { if (p has some property) matches++; }).start(); // Illegal to mutate matches

因為可變的變量在lambda表達式中不是線程安全的缭黔,這和內(nèi)部類的要求是一致的食茎,內(nèi)部類中只能引用外部定義的final變量;

lambda表達式的作用域與嵌套代碼塊的作用域是一樣的馏谨,所以在lambd表達式中的參數(shù)名或變量名不能與局部變量沖突别渔,如:

Path first = Paths.get("/usr/bin");
Comparator<String> comp = (first, second) -> Integer.compare(first.length(), second.length()); // Error: Variable first already defined

如果在lambda表達式中引用this變量,則引用的是創(chuàng)建該lambda表達式的方法的this變量,如:

public class Application() {
    public void doWork() {
        Runnable runner = () -> {
            ...;
            System.out.println(this.toString());
            ...
        };
    }
}

所以這里的this.toString()調(diào)用的是Application對象的toString()哎媚,而不是Runnable對象的喇伯。

1.7 Default Method

接口中只能有抽象方法,如果在已有的接口中新增一個方法拨与,則該接口所有的實現(xiàn)類都需要實現(xiàn)該方法稻据。Java 8中引入了Default Method的概念,在接口中新增一個default方法买喧,不會破壞已有的接口規(guī)則捻悯,接口的實現(xiàn)類可以選擇重寫或直接繼承該default方法,比如:

interface Person {
    long getId();
    default String getName() { return "John Q. Public"; }
}

Java是允許多繼承的淤毛,如果一個類的父類中定義的方法和接口中定義的default方法完全相同今缚,或者一個類的兩個接口中定義了完全相同的方法, 則如何處理這種沖突呢低淡?處理規(guī)則如下:

  • 如果是父類和接口的方法沖突:以父類中的方法為準荚斯,接口中的方法被忽略;
  • 如果兩個接口中的default方法沖突查牌,則需要重寫該方法解決沖突事期;

1.8 Static Method

Java 8之前,接口中只能定義static變量纸颜,Java 8開始兽泣,接口中可以添加static方法,比如Comparator接口新增了一系列comparingXXXstatic方法胁孙,比如:

public static <T> Comparator<T> comparingInt(ToIntFunction<? super T> keyExtractor) {
    Objects.requireNonNull(keyExtractor);
    return (Comparator<T> & Serializable)
      (c1, c2) -> Integer.compare(keyExtractor.applyAsInt(c1), keyExtractor.applyAsInt(c2));
}

使用這個static方法唠倦,以下兩種方式也是等價的:

Arrays.sort(cities, (first, second) -> Integer.compare(first.length(), second.length()));
Arrays.sort(cities, Comparator.comparingInt(String::length));

所以,以后我們在設計自己的接口時涮较,不需要再定義單獨的工具類(如Collections/Collection)稠鼻,在接口中使用static方法就行了。

本文來自我的博客狂票,轉(zhuǎn)載請注明出處候齿,謝謝!

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末闺属,一起剝皮案震驚了整個濱河市慌盯,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌掂器,老刑警劉巖亚皂,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異国瓮,居然都是意外死亡灭必,警方通過查閱死者的電腦和手機狞谱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來禁漓,“玉大人跟衅,你說我怎么就攤上這事×Пィ” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵肪康,是天一觀的道長荚恶。 經(jīng)常有香客問我,道長磷支,這世上最難降的妖魔是什么谒撼? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮雾狈,結(jié)果婚禮上廓潜,老公的妹妹穿的比我還像新娘。我一直安慰自己善榛,他們只是感情好辩蛋,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著移盆,像睡著了一般悼院。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上咒循,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天据途,我揣著相機與錄音,去河邊找鬼叙甸。 笑死颖医,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的裆蒸。 我是一名探鬼主播熔萧,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼僚祷!你這毒婦竟也來了哪痰?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤久妆,失蹤者是張志新(化名)和其女友劉穎晌杰,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體筷弦,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡肋演,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年抑诸,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片爹殊。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡蜕乡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出梗夸,到底是詐尸還是另有隱情层玲,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布反症,位于F島的核電站辛块,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏铅碍。R本人自食惡果不足惜润绵,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望胞谈。 院中可真熱鬧尘盼,春花似錦、人聲如沸烦绳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽碉就。三九已至疲恢,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間睹晒,已是汗流浹背趟庄。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留伪很,地道東北人戚啥。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像锉试,于是被迫代替她去往敵國和親猫十。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

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