1. 程式人生 > >java8的lambda表示式及方法引用(一)

java8的lambda表示式及方法引用(一)

當前很多公司的java開發環境都升級到jdk8以上了。lambda表示式是java8中最重要的更新,其目的是為了配合隨著並行運算流行起來的所謂“函式式”程式設計改進而來的語法糖。既然是語法糖,那麼其實不用這些lambda表示式也是可以實現原有功能的,只不過看起來程式碼行數多一些而已。

簡單說一下對lambda表示式的理解:lambda表示式其實就是“內部匿名類”物件的特定方法的實現程式碼。這個內部匿名類實現了某個特定介面(由註解@FunctionalInterface標註的)。這個帶@FunctionalInterface註解的介面只能有一個方法(否則就不知道lambda表示式要幹什麼事了)--嚴格來說是隻能有一個抽象方法,因為現在允許接口裡有預設實現了,帶預設實現那些不算。還是用程式碼描述更清楚:

假定一個介面定義:

@FunctionalInterface
public interface LamDemo {
	int operation(int a,int b);
}

如果不加@FunctionalInterface註解,那麼就是一個普通介面,加上了裡面的(抽象)方法就只能有一個。上面例子是一個名為operation的方法,兩個整數引數,返回值也是整數。

最普通的用法,就是寫一個類實現這個介面,然後用的時候建立這個類的物件:

//普通非匿名類實現介面LamDemo
class TempClass implements LamDemo{
	@Override
	public int operation(int a, int b) {
		return a+b;
	}
}

用的時候:

//普通物件,常規用法
private LamDemo ldTemp = new TempClass();

匿名類就為了省一點事,不去定義這個TempClass,直接用介面建立物件:

//建立匿名類物件賦予介面型別的變數
private LamDemo _ld = new LamDemo(){
	@Override
	public int operation(int a, int b) {
		return a+b;
	}
};

效果等同。

用lambda表示式更進一步,連new這個操作也隱含了:

//在變數中使用lambda表示式
private LamDemo ld=(int a ,int b)-> a+b;

隱含的意思是建立了一個物件,這個物件的類實現了LamDemo介面定義的方法,實現程式碼是a+b。這樣就明白為啥介面只能有一個方法了,否則編譯器不知道對應哪個。實現程式碼的規則是這樣的()->{},左邊是方法引數,右邊是方法體。這是標準寫法,但為啥感覺看到的lambda表示式五花八門似的。這就涉及各種各樣的所謂簡化規則。按標準寫法上面的例子其實應該寫成這樣:

//在變數中使用lambda表示式
private LamDemo ld=(int a ,int b)->{return a+b;};

簡化規則如下:

引數可以是零個或多個,零個時左邊就是一個空()

引數型別可指定,也可省略(因為介面方法定義裡已經說了引數型別了)

引數包含在圓括號中,用逗號分隔,一個引數時可省略()

表示式主體可以是零條或多條語句,包含在花括號中,只有一條語句時可省略{}

表示式主體有一條以上語句時,表示式的返回型別與程式碼塊的返回型別一致

表示式只有一條語句時,表示式的返回型別與該語句的返回型別一致

總之就是能犯懶就犯懶,少敲幾個字母是幾個字母,代價就是初學看起來有點暈。

既然lambda表示式實際上是個匿名物件,那麼它就可以賦給變數(上面例子);

可以當作引數傳給方法:

//lambda表示式作為方法引數
System.out.println(t.lambdaReturn1(3,5,(a,b)->a*b));

還可以用作方法返回值:

//在返回值中應用lambda expression
public LamDemo lambdaReturn2(){
	return (int a,int b)->a*b;
}

完整示例程式碼:

//普通非匿名類實現介面LamDemo
class TempClass implements LamDemo{
	@Override
	public int operation(int a, int b) {
		return a+b;
	}
}
public class Test {
	//普通類物件
	private LamDemo ldTemp = new TempClass();
	
	//匿名類物件
	private LamDemo _ld = new LamDemo(){
		@Override
		public int operation(int a, int b) {
			return a+b;
		}
	};
	
	//在變數中使用lambda表示式
	private LamDemo ld=(int a ,int b)->a+b;
		
	//在引數中使用lambda表示式(見後面呼叫)
	public int lambdaReturn1(int source,int target,LamDemo lr){
		return lr.operation(source, target);
	}
	
	//在返回值中應用lambda expression
	public LamDemo lambdaReturn2(){
		return (int a,int b)->a*b;
	}
	
	//省略引數型別定義
	private LamDemo ld1=(a ,b)-> a+b;
		
	public static void main(String[] args){
		Test t=new Test();
		System.out.println(t.ld.operation(3, 5));
		System.out.println(t.ld1.operation(6, 6));
		
		//lambda表示式作為方法引數
		System.out.println(t.lambdaReturn1(3,5,(a,b)->a*b));
		
		//方法返回值是個lambda表示式(是個物件)
		System.out.println(t.lambdaReturn2().operation(5,7));
		
		//lambda表示式方式建立新執行緒並執行(lambda expression作為實現Runnable介面的物件傳給Thread的建構函式,其對應唯一方法是void run(),無引數無返回值)
		new Thread(()->System.out.println("lambda expression thread run")).start();
		
		/*
		 * 總結:lambda表示式就是把一段程式碼作為內部類的物件的某個特定實現(編譯器所推匯出的函式介面)
		 * lambda expression實際上是用內部類實現的,所以傳過來的引數預設是final的,不可修改
		 * lambda表示式與匿名內部類的區別在於this識別符號:在匿名內部類中使用this指的是此內部類;
		 * 而在lambda表示式中的this識別符號指的是外部呼叫者
		 */
	}
}