Lambda 表達式(也稱爲閉包),它容許咱們將函數當成參數傳遞給某個方法,或者把代碼自己看成數據處理。不少語言(Groovy、Scala等)從設計之初就支持 Lambda 表達式。可是 java 中使用的是匿名內部類代替。最後藉助強大的社區力量,找了一個折中的 Lambda 實現方案,能夠實現簡潔而緊湊的語言結構。java
匿名內部類,即一個沒有名字的,存在於一個類或方法內部的類。當咱們須要用某個類且只須要用一次,建立和使用合二爲一時,咱們能夠選擇匿名內部類,省掉咱們定義類的步驟。程序員
匿名內部類會隱式的繼承一個類或實現一個接口,或者說匿名內部類是一個繼承了該類或者實現了該接口的子類匿名對象。下面看一個匿名內部類的例子:閉包
測試類中調用方法dom
package com.lotbyte.main; /* 定義和使用匿名內部類 */ public class NoNameClass { public static void main(String[] args) { Model m = new Model(){ @Override public void func() { System.out.println("方法的實現"); } }; m.func(); } } // 須要被實現的接口 interface Model{ void func(); }
從某種意義上來講,Lambda 表達式能夠看做是匿名內部類對象的簡寫形式。最簡單的 Lambda 表達式能夠由用逗號分隔的參數列表
、->
符號和語句塊
組成。ide
注意:此時匿名內部類只能實現接口,不能是繼承抽象類函數
例如將上面的例子作一個簡化,使用 Lambda 的形式以下:學習
public class NonameClassForLambda { public static void main(String[] args) { // Lambda方式簡寫,方法實現能夠很簡單 Model1 md = ()-> System.out.println("hello"); md.func(); // 也能夠是比較複雜的操做 md = () -> { for (int i = 1; i <=5; i++) { System.out.println(i); } }; md.func(); } } // 接口 interface Model1{ void func(); }
以上是一個簡單的 Lambda 的書寫形式,()
中是形參列表,沒有則爲空括號, ->
爲語法格式,以後則爲方法的實現(一條語句能夠直接書寫,當有多條語句時,須要使用{}
進行包裹)。從這能夠看出在接口中必須只能存在一個抽象方法。測試
注意:Lambda中必須有個接口spa
使用 Lambda 時,實現方法能夠有參數,也能夠有返回值,若是沒指定參數類型,則由編譯器自行推斷得出。設計
生成[1,10]之間的任意整數
interface Model2{ int func(); } Model2 md2 = () -> {return (int)(Math.random()*10+1)};
說明:Lambda 的改寫須要有對應的抽象方法,當沒有參數時須要使用()
佔位,當表達式只有一行代碼時,能夠省略return
和{}
以上的 Lambda 等價於:
Model2 md2 = () -> (int)(Math.random()*10+1);
返回一個對數字描述的字符串
interface Model3{ String func(int a); } Model3 md3 = (int a) -> { return "This is a number " + a; };
說明:形參寫在()
內便可,參數的類型能夠省略,此時將由編譯器自行推斷得出,同時還能夠省略()
md3 = a -> "This is a number " + a;
省略了參數類型,小括號,同時連帶實現體的括號和 return 都省了。
根據輸入的運算符計算兩個數的運算,並返回結果
interface Model4{ String func(int a, int b, String oper); } Model4 md4 = (a, b, s) -> { String res = ""; if("+".equals(s)){ res = ( a+b ) + ""; }else if("-".equals(s)){ res = ( a-b ) + ""; }else if("*".equals(s)){ res = ( a*b ) + ""; }else if("/".equals(s)){ res = ( a/b ) + ""; // 暫不考慮除0的狀況 }else{ res = "操做有失誤"; } return res; }; System.out.println(md4.func(1,1,"+"));
以上例子爲多個參數的 Lambda 表達式,其中省略掉了每個參數的類型,編譯器自動推斷。多條語句時實現體的{}
不能省。
最新技術學習資料( ^_^) → lezijie007(程序員暗號:1024)
在 jdk8 以前,接口能夠做爲方法參數傳入,執行時必須提供接口實現類的實例。從 java8 開始,Lambda 能夠做爲接口方法實現,看成參數傳入,不管從形式上仍是實際上都省去了對象的建立。使代碼更加的緊湊簡單高效。
使用 Lambda 表達式須要有如下幾步:
一、定義接口,抽象方法的模板;
二、在某方法中須要接口做爲參數;
三、調用方法時須要將抽象方法實現(此時咱們使用 Lambda 表達式)並傳入便可。
在接口中,必須有且僅有一個抽象方法,以肯定 Lambda 模板
// 無參無返回值的方法 interface LambdaInterface1{ void printString(); } // 帶參無返回值的方法 interface LambdaInterface2{ void printString(String str); }
在某方法中須要使用接口做爲參數
// 無參 public static void testLambda(LambdaInterface1 lam1){ lam1.printString(); } // 帶參 public static void testLambda2(String s,LambdaInterface2 lam2){ lam2.printString(s); }
使用方法時須要用 Lambda 將抽象方法實現
// 無參Lambda做爲參數 testLambda(()->{ System.out.println("能夠簡單,能夠複雜"); }); // 帶參Lambda做爲參數 testLambdaParam("hello",(a)->{ System.out.println(a); });
經過以上三步,可以完整地展現 Lambda 如何演變而來。此後在使用時,jdk 中已經提供不少場景了,即前兩部已經完成,咱們更多的是實現第三步便可。
例如以 ArrayList 的遍歷爲例子,分析 Lambda 的使用方式。
public static void main(String[] args) { List<String> strs = new ArrayList<String>(){ { add("aaa"); add("bbb"); add("ccc"); } }; strs.forEach((str)-> System.out.println(str)); }
下面看看 forEach 的源碼,定義中使用了接口 Consumer 做爲參數,並調用了其方法:
Consumer 中的抽象方法只有 accept 一個:
經過在 forEach 方法中調用 Consumer 的 accept 方法,並將每個元素做爲參數傳入,使得 accept 方法能夠對每個元素進行操做,當咱們使用 Lambda 實現 accept 時就變成了咱們本身對每個元素的處理了。咱們只負責處理便可。
在 Lambda 中能夠定義本身的局部變量,也可使用外層方法的局部變量,還可使用屬性。這一點也不難理解,既然是一個方法的實現,只寫了一個代碼塊,那麼使用自己所屬方法的局部變量和類的屬性也並不過度。
public static void main(String[] args) { List<String> strs = new ArrayList<String>(){ { add("aaa"); add("bbb"); add("ccc"); } }; int j = 1; strs.forEach((str)->{ int i = 0; System.out.println(str + " " + i + " " + j); }); }
注意:此時外部局部變量將自動變爲 final
例子:返回判斷字符串是否爲空
public class Demo004_2 { public static void main(String[] args) { System.out.println(testLambda().isEmpty("string")); } // 判斷字符串是否爲空 public static AssertEmpty testLambda(){ return (n)-> null==n||n.trim().isEmpty(n); } } interface AssertEmpty{ boolean isEmpty(String str); }