JDK8新特性【Lambda表達(dá)式和函數(shù)式接口】

一罚勾、Lambda表達(dá)式

Lambda表達(dá)式(也稱為閉包),lambda表達(dá)式本質(zhì)上是一個(gè)匿名方法。它允許我們將函數(shù)當(dāng)成參數(shù)傳遞給某個(gè)方法吵血,或者把代碼本身當(dāng)作數(shù)據(jù)處理。

函數(shù)式開發(fā)者非常熟悉這些概念偷溺。很多JVM平臺(tái)上的語言(Groovy蹋辅、Scala等)從誕生之日就支持Lambda表達(dá)式,但是Java開發(fā)者沒有選擇挫掏,只能使用匿名內(nèi)部類代替Lambda表達(dá)式侦另。

1. 語法

lambda 表達(dá)式的語法格式如下:

(parameters) -> expression

(parameters) ->{ statements; }

在最簡單的形式中,一個(gè)lambda可以由:用逗號(hào)分隔的參數(shù)列表–>符號(hào)褒傅、函數(shù)體三部分表示

Arrays.asList( "a", "b", "d" ).forEach( e -> System.out.println( e ) );

如果Lambda表達(dá)式需要更復(fù)雜的語句塊弃锐,則可以使用花括號(hào)將該語句塊括起來,類似于Java中的函數(shù)體

Arrays.asList( "a", "b", "d" ).forEach( e -> {
    System.out.print( e );
    System.out.print( e );
} );

2. 以下是lambda表達(dá)式的重要特征:

  • 可選類型聲明:不需要聲明參數(shù)類型殿托,編譯器可以統(tǒng)一識(shí)別參數(shù)值霹菊。
  • 可選的參數(shù)圓括號(hào):一個(gè)參數(shù)無需定義圓括號(hào),但多個(gè)參數(shù)需要定義圓括號(hào)支竹。
  • 可選的大括號(hào):如果主體包含了一個(gè)語句旋廷,就不需要使用大括號(hào)。
  • 可選的返回關(guān)鍵字:如果主體只有一個(gè)表達(dá)式返回值則編譯器會(huì)自動(dòng)返回值礼搁,大括號(hào)需要指定明表達(dá)式返回了一個(gè)數(shù)值柳洋。

3. Lambda 表達(dá)式的簡單例子:

// 1. 不需要參數(shù),返回值為 5  
() -> 5  
  
// 2. 接收一個(gè)參數(shù)(數(shù)字類型),返回其2倍的值  
x -> 2 * x  
  
// 3. 接受2個(gè)參數(shù)(數(shù)字),并返回他們的差值  
(x, y) -> x – y  
  
// 4. 接收2個(gè)int型整數(shù),返回他們的和  
(int x, int y) -> x + y  
  
// 5. 接受一個(gè) string 對(duì)象,并在控制臺(tái)打印,不返回任何值(看起來像是返回void)  
(String s) -> System.out.print(s)

4. demo

使用 Lambda 表達(dá)式需要注意以下兩點(diǎn):

Lambda 表達(dá)式主要用來定義行內(nèi)執(zhí)行的方法類型接口,例如叹坦,一個(gè)簡單方法接口熊镣。在下面例子中,我們使用各種類型的Lambda表達(dá)式來定義MathOperation接口的方法募书。然后我們定義了sayMessage的執(zhí)行绪囱。
Lambda 表達(dá)式免去了使用匿名方法的麻煩,并且給予Java簡單但是強(qiáng)大的函數(shù)化的編程能力莹捡。

public class Java8Tester {
   public static void main(String args[]){
      Java8Tester tester = new Java8Tester();
        
      // 類型聲明
      MathOperation addition = (int a, int b) -> a + b;
        
      // 不用類型聲明
      MathOperation subtraction = (a, b) -> a - b;
        
      // 大括號(hào)中的返回語句
      MathOperation multiplication = (int a, int b) -> { return a * b; };
        
      // 沒有大括號(hào)及返回語句
      MathOperation division = (int a, int b) -> a / b;
        
      System.out.println("10 + 5 = " + tester.operate(10, 5, addition));
      System.out.println("10 - 5 = " + tester.operate(10, 5, subtraction));
      System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication));
      System.out.println("10 / 5 = " + tester.operate(10, 5, division));
        
      // 不用括號(hào)
      GreetingService greetService1 = message ->
      System.out.println("Hello " + message);
        
      // 用括號(hào)
      GreetingService greetService2 = (message) ->
      System.out.println("Hello " + message);
        
      greetService1.sayMessage("Runoob");
      greetService2.sayMessage("Google");
   }
    
   interface MathOperation {
      int operation(int a, int b);
   }
    
   interface GreetingService {
      void sayMessage(String message);
   }
    
   private int operate(int a, int b, MathOperation mathOperation){
      return mathOperation.operation(a, b);
   }
}

執(zhí)行以上腳本鬼吵,輸出結(jié)果為:

$ javac Java8Tester.java 
$ java Java8Tester
10 + 5 = 15
10 - 5 = 5
10 x 5 = 50
10 / 5 = 2
Hello Runoob
Hello Google

5. 變量作用域

接口變量必須是 publicstatic篮赢、final 修飾的

lambda 表達(dá)式只能引用標(biāo)記了 final 的外層局部變量齿椅,這就是說不能在 lambda 內(nèi)部修改定義在域外的局部變量,否則會(huì)編譯錯(cuò)誤启泣。

在 Java8Tester.java 文件輸入以下代碼:

public class Java8Tester {
 
   final static String salutation = "Hello! ";
   
   public static void main(String args[]){
      GreetingService greetService1 = message -> 
      System.out.println(salutation + message);
      greetService1.sayMessage("Runoob");
   }
    
   interface GreetingService {
      void sayMessage(String message);
   }
}

執(zhí)行以上腳本涣脚,輸出結(jié)果為:

$ javac Java8Tester.java 
$ java Java8Tester
Hello! Runoob

我們也可以直接在 lambda 表達(dá)式中訪問外層的局部變量:

public class Java8Tester {
    public static void main(String args[]) {
        final int num = 1;
        Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num));
        s.convert(2);  // 輸出結(jié)果為 3
    }
 
    public interface Converter<T1, T2> {
        void convert(int i);
    }
}

lambda 表達(dá)式的局部變量可以不用聲明為 final,但是必須不可被后面的代碼修改(即隱性的具有 final 的語義)

int num = 1;  
Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num));
s.convert(2);
num = 5;  
//報(bào)錯(cuò)信息:Local variable num defined in an enclosing scope must be final or effectively  final

在 Lambda 表達(dá)式當(dāng)中不允許聲明一個(gè)與局部變量同名的參數(shù)或者局部變量寥茫。

String first = "";  
Comparator<String> comparator = (first, second) -> Integer.compare(first.length(), second.length());  //編譯會(huì)出錯(cuò) 



二遣蚀、#函數(shù)式接口

Lambda的設(shè)計(jì)者們?yōu)榱俗尙F(xiàn)有的功能與Lambda表達(dá)式良好兼容,考慮了很多方法纱耻,于是產(chǎn)生了函數(shù)接口這個(gè)概念芭梯。函數(shù)接口指的是只有一個(gè)函數(shù)的接口,這樣的接口可以隱式轉(zhuǎn)換為Lambda表達(dá)式弄喘。

使用lambda表達(dá)式玖喘,新建一個(gè)類的時(shí)候,必須滿足以下三點(diǎn):

  • 新建的類必須是接口蘑志,此接口稱為函數(shù)式接口
  • 此接口只能有一個(gè)非抽象類接口累奈,但是可以使用default參數(shù)添加擴(kuò)展方法
  • 忽略object類方法

1. 什么是函數(shù)式接口

所謂的函數(shù)式接口贬派,當(dāng)然首先是一個(gè)接口,然后就是在這個(gè)接口里面只能有一個(gè)抽象方法费尽。
這種類型的接口也稱為SAM接口,即Single Abstract Method interfaces

2. 測(cè)試

  • 準(zhǔn)備一個(gè)接口
public interface MyTest {
    void test1();
}
  • 使用lambda表達(dá)式實(shí)現(xiàn)該接口
public static void main(String[] args) {
    MyTest myTest = () -> System.out.println("hello world!");
    myTest.test1();
}

得到輸出為:hello world!

3. 如果接口類里面有多個(gè)方法行不行呢羊始?--- 可以

默認(rèn)方法和靜態(tài)方法不會(huì)破壞函數(shù)式接口的定義

  • 在接口中加入一個(gè)方法
public interface MyTest {
    void test1();
    void test2();
}

會(huì)看到報(bào)如下錯(cuò)誤
Multiple non-overriding abstract methods found in interface--在接口中找到多個(gè)非重寫抽象方法
找到重點(diǎn)詞多個(gè)抽象方法旱幼,那就是需要將抽象方法減少嘍,使用default關(guān)鍵字突委,改造代碼如下:

public interface MyTest {
    void test1();
    default void test2() {
        System.out.println("Bye bye world!");
    }
}

再次調(diào)用

public static void main(String[] args) {
    MyTest myTest = () -> System.out.println("hello world!");
    myTest.test1();
    myTest.test2();
}

out:

hello world!
Bye bye world!

4. 如何聲明一個(gè)接口為函數(shù)式接口

在接口上使用@FunctionalInterface柏卤,這樣會(huì)在接口不符合函數(shù)式接口定義規(guī)范的時(shí)候就報(bào)錯(cuò)。

@FunctionalInterface
public interface Functional {
    void method();
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末匀油,一起剝皮案震驚了整個(gè)濱河市缘缚,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌敌蚜,老刑警劉巖桥滨,帶你破解...
    沈念sama閱讀 219,539評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異弛车,居然都是意外死亡齐媒,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門纷跛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來喻括,“玉大人,你說我怎么就攤上這事贫奠』Q” “怎么了?”我有些...
    開封第一講書人閱讀 165,871評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵唤崭,是天一觀的道長拷恨。 經(jīng)常有香客問我,道長谢肾,這世上最難降的妖魔是什么挑随? 我笑而不...
    開封第一講書人閱讀 58,963評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮勒叠,結(jié)果婚禮上兜挨,老公的妹妹穿的比我還像新娘。我一直安慰自己眯分,他們只是感情好拌汇,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,984評(píng)論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著弊决,像睡著了一般噪舀。 火紅的嫁衣襯著肌膚如雪魁淳。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,763評(píng)論 1 307
  • 那天与倡,我揣著相機(jī)與錄音界逛,去河邊找鬼。 笑死纺座,一個(gè)胖子當(dāng)著我的面吹牛息拜,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播净响,決...
    沈念sama閱讀 40,468評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼少欺,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了馋贤?” 一聲冷哼從身側(cè)響起赞别,我...
    開封第一講書人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎配乓,沒想到半個(gè)月后仿滔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,850評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡犹芹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,002評(píng)論 3 338
  • 正文 我和宋清朗相戀三年堤撵,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片羽莺。...
    茶點(diǎn)故事閱讀 40,144評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡实昨,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出盐固,到底是詐尸還是另有隱情荒给,我是刑警寧澤,帶...
    沈念sama閱讀 35,823評(píng)論 5 346
  • 正文 年R本政府宣布刁卜,位于F島的核電站志电,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏蛔趴。R本人自食惡果不足惜挑辆,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,483評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望孝情。 院中可真熱鬧鱼蝉,春花似錦、人聲如沸箫荡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽羔挡。三九已至洁奈,卻和暖如春间唉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背利术。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評(píng)論 1 272
  • 我被黑心中介騙來泰國打工呈野, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人印叁。 一個(gè)月前我還...
    沈念sama閱讀 48,415評(píng)論 3 373
  • 正文 我出身青樓被冒,卻偏偏與公主長得像,于是被迫代替她去往敵國和親喉钢。 傳聞我的和親對(duì)象是個(gè)殘疾皇子姆打,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,092評(píng)論 2 355