Lambda 表達式(也稱為閉包),它允許我們將函數當成參數傳遞給某個方法,或者把代碼本身當作數據處理。很多語言(Groovy、Scala等)從設計之初就支持 Lambda 表達式。但是 java 中使用的是匿名內部類代替。最後藉助強大的社區力量,找了一個折中的 Lambda 實現方案,可以實現簡潔而緊湊的語言結構。
一、匿名內部類到Lambda的演化
匿名內部類,即一個沒有名字的,存在於一個類或方法內部的類。當我們需要用某個類且只需要用一次,創建和使用合二為一時,我們可以選擇匿名內部類,省掉我們定義類的步驟。
匿名內部類會隱式的繼承一個類或實現一個接口,或者說匿名內部類是一個繼承了該類或者實現了該接口的子類匿名對象。下面看一個匿名內部類的例子:
測試類中調用方法
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 表達式可以看作是匿名內部類對象的簡寫形式。最簡單的 Lambda 表達式可以由用
逗號分隔的參數列表
、
->
符號和
語句塊
組成。
注意:此時匿名內部類只能實現接口,不能是繼承抽象類
例如將上面的例子做一個簡化,使用 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中必須有個接口
三、Lambda的形式
使用 Lambda 時,實現方法可以有參數,也可以有返回值,如果沒指定參數類型,則由編譯器自行推斷得出。
3.1、 無參帶返回值
生成[1,10]之間的任意整數
interface Model2{
int func();
}
Model2 md2 = () -> {return (int)(Math.random()*10+1)};
說明:Lambda 的改寫需要有對應的抽象方法,當沒有參數時需要使用
()
佔位,當表達式只有一行代碼時,可以省略
return
和
{}
以上的 Lambda 等價於:
Model2 md2 = () -> (int)(Math.random()*10+1);
3.2 、帶參帶返回值
返回一個對數字描述的字符串
interface Model3{
String func(int a);
}
Model3 md3 = (int a) -> {
return "This is a number " + a;
};
說明:形參寫在
()
內即可,參數的類型可以省略,此時將由編譯器自行推斷得出,同時還可以省略
()
md3 = a -> "This is a number " + a;
省略了參數類型,小括號,同時連帶實現體的括號和 return 都省了。
3.3 、帶多個參數
根據輸入的運算符計算兩個數的運算,並返回結果
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)
四、Lambda作為參數
在 jdk8 之前,接口可以作為方法參數傳入,執行時必須提供接口實現類的實例。從 java8 開始,Lambda 可以作為接口方法實現,當作參數傳入,無論從形式上還是實際上都省去了對象的創建。使代碼更加的緊湊簡單高效。
使用 Lambda 表達式需要有以下幾步:
1、定義接口,抽象方法的模板;
2、在某方法中需要接口作為參數;
3、調用方法時需要將抽象方法實現(此時我們使用 Lambda 表達式)並傳入即可。
4.1、定義接口
在接口中,必須有且僅有一個抽象方法,以確定 Lambda 模板
// 無參無返回值的方法
interface LambdaInterface1{
void printString();
}
// 帶參無返回值的方法
interface LambdaInterface2{
void printString(String str);
}
4.2、定義方法接收參數
在某方法中需要使用接口作為參數
// 無參
public static void testLambda(LambdaInterface1 lam1){
lam1.printString();
}
// 帶參
public static void testLambda2(String s,LambdaInterface2 lam2){
lam2.printString(s);
}
4.3、Lambda實現
使用方法時需要用 Lambda 將抽象方法實現
// 無參Lambda作為參數
testLambda(()->{
System.out.println("可以簡單,可以複雜");
});
// 帶參Lambda作為參數
testLambdaParam("hello",(a)->{
System.out.println(a);
});
通過以上三步,能夠完整地展示 Lambda 如何演變而來。此後在使用時,jdk 中已經提供很多場景了,即前兩部已經完成,我們更多的是實現第三步即可。
五、forEach展示Lambda
例如以 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中使用變量
在 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
七、Lambda作為方法返回值
例子:返回判斷字符串是否為空
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);
}