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