1. 背景
Lambda表達式是JavaSE 8中一個重要的新特性。Lambda表達式允許你通過表達式來代替功能接口。 Lambda表達式就和方法一樣,它提供了一個正常的參數列表和一個使用這些參數的主體(body,可以是一個表達式或一個代碼塊)。 Lambda 表達式(Lambda expression),基于數學中的?λ?演算得名,也可稱為閉包(Closure)。
1.1 Lambda表達式的語法
基本語法: (parameters) -> expression 或 (parameters) ->{ statements; }
Lambda表達式由三部分組成:
1. paramaters:類似方法中的形參列表,這里的參數是函數式接口里的參數。這里的參數類型可以明確的聲明也可不聲明而由JVM隱含的推斷。另外當只有一個推斷類型時可以省略掉圓括號。
2. ->:可理解為“被用于”的意思
3. 方法體:可以是表達式也可以代碼塊,是函數式接口里方法的實現。代碼塊可返回一個值或者什么都不返回,這里的代碼塊等同于方法的方法體。如果是表達式,也可以返回一個值或者什么都不返回。
// 1. 不需要參數,返回值為 2() -> 2// 2. 接收一個參數(數字類型),返回其2倍的值x -> 2 * x// 3. 接受2個參數(數字),并返回他們的和(x, y) -> x + y// 4. 接收2個int型整數,返回他們的乘積(int x, int y) -> x * y// 5. 接受一個 string 對象,并在控制臺打印,不返回任何值(看起來像是返回void)(String s) -> System.out.print(s)
1.2 函數式接口
要了解Lambda表達式,首先需要了解什么是函數式接口,函數式接口定義:一個接口有且只有一個抽象方法。
注意:
- 如果一個接口只有一個抽象方法,那么該接口就是一個函數式接口
- 如果我們在某個接口上聲明了 @FunctionalInterface 注解,那么編譯器就會按照函數式接口的定義來要求該接 口,這樣如果有兩個抽象方法,程序編譯就會報錯的。所以,從某種意義上來說,只要你保證你的接口中只有一個抽象方法,你可以不加這個注解。加上就會自動進行檢測。
定義方式:
@FunctionalInterfaceinterface NoParameterNoReturn {//注意:只能有一個方法void test();}
特例:
@FunctionalInterfaceinterface NoParameterNoReturn {void test();default void test2() {System.out.println("JDK1.8新特性,default默認方法可以有具體的實現");}}
2. Lambda表達式的基本使用
準備幾個接口:
//無返回值無參數
@FunctionalInterface
interface NoParameterNoReturn {void test();
}
//無返回值一個參數
@FunctionalInterface
interface OneParameterNoReturn {void test(int a);
}
//無返回值多個參數
@FunctionalInterface
interface MoreParameterNoReturn {void test(int a,int b);
}
//有返回值無參數
@FunctionalInterface
interface NoParameterReturn {int test();
}
//有返回值一個參數
@FunctionalInterface
interface OneParameterReturn {int test(int a);
}
//有返回值多參數
@FunctionalInterface
interface MoreParameterReturn {int test(int a,int b);
}
Lambda可以理解為:Lambda就是匿名內部類的簡化,實際上是創建了一個類,實現了接口,重寫了接口的方法。沒有使用Lambda表達式的時候的調用方式:
NoParameterNoReturn noParameterNoReturn = new NoParameterNoReturn(){@Overridepublic void test() {System.out.println("hello");}
};
noParameterNoReturn.test();
使用Lambda表達式:
public class TestDemo {public static void main(String[] args) {NoParameterNoReturn noParameterNoReturn = ()->System.out.println("無參數無返回值");noParameterNoReturn.test();OneParameterNoReturn oneParameterNoReturn = (int a)->System.out.println("一個參數無返回值:"+ a);oneParameterNoReturn.test(10);MoreParameterNoReturn moreParameterNoReturn = (int a,int b)->System.out.println("多個參數無返回值:"+a+" "+b);moreParameterNoReturn.test(20,30);//有返回值無參數!NoParameterReturn noParameterReturn = ()->10;System.out.println(noParameterReturn.test());//有返回值有一個參數!OneParameterReturn oneParameterReturn = a->a;oneParameterReturn.test(50);//有返回值多個參數!MoreParameterReturn moreParameterReturn = (a,b)->a+b;System.out.println(moreParameterReturn.test(60, 70));}
}
語法精簡tips:
- 參數類型可以省略,如果需要省略,每個參數的類型都要省略
- 參數的小括號里面只有一個參數,那么小括號可以省略
- 如果方法體當中只有一句代碼,那么大括號可以省略
- 如果方法體中只有一條語句,且是return語句,那么大括號可以省略,且去掉return關鍵字
3. 變量捕獲
?3.1 匿名內部類的變量捕獲
class Test {public void func(){System.out.println("func()");}
}
public class TestDemo {public static void main(String[] args) {int a = 100;new Test(){@Overridepublic void func() {System.out.println("我是內部類,且重寫了func這個方法!");System.out.println("我是捕獲到變量 a == "+a+" 我是一個常量,或者是一個沒有改變過值的變量!");}};}
}
在上述代碼當中的變量a就是,捕獲的變量。這個變量要么是被final修飾,如果不是被final修飾的 你要保證在使用 之前,沒有修改。如下代碼就是錯誤的代碼:
3.2 Lambda的變量捕獲
4. Lambda在集合中的使用
為了能夠讓Lambda和Java的集合類集更好的一起使用,集合當中也新增了部分接口,以便與Lambda表達式對接。
對應的接口 | 新增的方法 |
Collection | removeIf() spliterator() stream() parallelStream() forEach() |
List | replaceAll() sort() |
Map | getOrDefault() forEach() replaceAll() putIfAbsent() remove() replace() computeIfAbsent() computeIfPresent() compute() merge() |
4.1?Collection接口
forEach()方法演示
該方法在接口 Iterable 當中,原型如下:
default void forEach(Consumer<? super T> action) {Objects.requireNonNull(action);for (T t : this) {action.accept(t);}
}
該方法表示:對容器中的每個元素執行action指定的動作
public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();list.add("Hello");list.add("world");list.add("hello");list.add("lambda");list.forEach(new Consumer<String>(){@Overridepublic void accept(String str){//簡單遍歷集合中的元素。System.out.print(str+" ");}});
}
輸出結果:Hello world hello lambda
我們可以修改為如下代碼:
public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();list.add("Hello");list.add("bit");list.add("hello");list.add("lambda");//表示調用一個,不帶有參數的方法,其執行花括號內的語句,為原來的函數體內容。list.forEach(s -> {System.out.println(s);});
}
輸出結果:Hello world hello lambda
4.2 List接口
sort()方法的演示
sort方法源碼:該方法根據c指定的比較規則對容器元素進行排序
public void sort(Comparator<? super E> c) {final int expectedModCount = modCount;Arrays.sort((E[]) elementData, 0, size, c);if (modCount != expectedModCount) {throw new ConcurrentModificationException();}modCount++;
}
使用示例:
public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();list.add("Hello");list.add("world");list.add("hello");list.add("lambda");list.sort(new Comparator<String>() {@Overridepublic int compare(String str1, String str2){//注意這里比較長度return str1.length()-str2.length();}});System.out.println(list);
}
輸出結果:Hello, world, hello, lambda
修改為Lambda表達式:
public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();list.add("Hello");list.add("world");list.add("hello");list.add("lambda");
//調用帶有2個參數的方法,且返回長度的差值list.sort((str1,str2)-> str1.length()-str2.length());System.out.println(list);}
輸出結果:Hello, world, hello, lambda
4.3 Map接口
HashMap 的 forEach()
該方法原型如下:
default void forEach(BiConsumer<? super K, ? super V> action) {Objects.requireNonNull(action);for (Map.Entry<K, V> entry : entrySet()) {K k;V v;try {k = entry.getKey();v = entry.getValue();} catch(IllegalStateException ise) {// this usually means the entry is no longer in the map.throw new ConcurrentModificationException(ise);}action.accept(k, v);}
}
作用是對Map中的每個映射執行action指定的操作。
代碼示例:
public static void main(String[] args) {HashMap<Integer, String> map = new HashMap<>();map.put(1, "hello");map.put(2, "world");map.put(3, "hello");map.put(4, "lambda");map.forEach(new BiConsumer<Integer, String>(){@Overridepublic void accept(Integer k, String v){System.out.print(k + "=" + v + " ");}});}
輸出結果: 1=hello 2=world 3=hello 4=lambda?
使用lambda表達式后的代碼:
public static void main(String[] args) {HashMap<Integer, String> map = new HashMap<>();map.put(1, "hello");map.put(2, "world");map.put(3, "hello");map.put(4, "lambda");map.forEach((k,v)-> System.out.print(k + "=" + v + " "));}
輸出結果:1=hello 2=world 3=hello 4=lambda?
5. 總結
Lambda表達式的優點很明顯,在代碼層次上來說,使代碼變得非常的簡潔。缺點也很明顯,代碼不易讀。
優點:
- 1. 代碼簡潔,開發迅速
- 2. 方便函數式編程
- 3. 非常容易進行并行計算
- 4. Java 引入 Lambda,改善了集合操作
缺點:
- 1. 代碼可讀性變差
- 2. 在非并行計算中,很多計算未必有傳統的 for 性能要高
- 3. 不容易進行調試