Java8InAction-行為引數化與Lambda
1、摘要
用更少的程式碼幹更多的事,一直是程式設計師的追求。
但是很多時候,需求的變化,很容易讓我們寫出高度冗餘的程式碼。
很多函式可能就是幾句核心程式碼不一樣,複製貼上稍微改改就行。
行為引數化,就是把行為封裝起來,然後作為引數傳遞。
策略模式就是一種行為引數化的實現,將行為封裝在不同的類中,然後通過傳遞類來傳遞行為。
但是,為了封裝行為而寫一個類,有點大材小用,寫了很多不必要的程式碼。
我們有沒有一種能夠直接傳遞行為的方法呢?
有,那就是Lambda表示式。它可以封裝一個行為並可以進行傳遞。
Lambda為什麼這麼神奇呢?
實際上Lambda只能稱得上是一個Java的語法糖,
它只是將我們建立Java類,生成Java物件的操作交給了JVM來實現,簡化了我們的工作,
但是並沒有新增新的特性,自己封裝類做不到的它也做不到。
在JVM執行到lambda表示式時,它會根據上下文中的型別資訊,將表示式繫結到一個介面(函式式介面),
然後,再使用動態生成位元組碼的方式,建立一個實現了該介面的類來封裝表示式的功能,
最後,再建立該類的的一個物件。
2、行為引數化
阿拉丁神燈最近很煩躁,因為對許願者的每一個願望,神燈都要念一段長長的咒語。
先是要念咒語來激發神力,然後才能使用神力來實現願望,最後還要念咒語收回神力。
可是,隨著神力越來越強大,神燈需要念的咒語也越來越長了。
經常會出現唸咒兩小時,實現願望兩分鐘的情況。
許願者的每次都要等很久,差評愈來愈多了。
於是神燈去找了智慧之神。
public class Ald { void wish1(){ System.out.println("power activated!"); System.out.println("a"); System.out.println("power deactivated!"); } void wish2(){ System.out.println("power activated!"); System.out.println("b"); System.out.println("power deactivated!"); } void wish3(){ System.out.println("power activated!"); System.out.println("c"); System.out.println("power deactivated!"); } }
智慧之神看了神燈實現願望後,說,你也太笨了吧,就不會發明一個自動咒語機嘛。
只用傳入願望就行了,咒語機自動念咒語。
public class Ald { void wish1(String a){ System.out.println("power activated!"); System.out.println(a); System.out.println("power deactivated!"); } }
使用了自動咒語機一段時間之後,神燈還是不太滿意。
機器一次只能傳入一個咒語,但是,好多願望要好幾個咒語才能實現。
因此,它又去找了智慧之神。
智慧之神不想跟他說話,並扔給他又一個咒語機。
interface wish{ public void handleWish(); } class wish1 implements wish{ @Override public void handleWish() { System.out.println("哇啦哇啦"); System.out.println("wish2!"); System.out.println("巴卡巴卡"); } } class wish2 implements wish{ @Override public void handleWish() { System.out.println("烏拉烏拉"); System.out.println("wish2!"); System.out.println("巴卡巴卡"); } } public class Ald { void wish1(wish a){ System.out.println("power activated!"); a.handleWish(); System.out.println("power deactivated!"); } }
。。。編不下去了。。。。還是看程式碼吧。。。
使用策略模式雖然更加的清晰了,但是,卻不免要建立一些策略類,
對於不會反覆使用的策略,還要建立一個類,是不大划算的。
interface wish{ public void handleWish(); } public class Ald { void wish(wish a){ System.out.println("power activated!"); a.handleWish(); System.out.println("power deactivated!"); } public static void main(String[] args) { Ald ald=new Ald(); ald.wish(new wish() { @Override public void handleWish() { System.out.println("烏拉烏拉"); System.out.println("wish2!"); System.out.println("巴卡巴卡"); } }); } }
使用匿名類可以簡化建立類的程式碼。
interface wish{ public void handleWish(); } public class Ald { void wish(wish a){ System.out.println("power activated!"); a.handleWish(); System.out.println("power deactivated!"); } public static void main(String[] args) { Ald ald=new Ald(); ald.wish(() -> { System.out.println("烏拉烏拉"); System.out.println("wish2!"); System.out.println("巴卡巴卡"); }); } }
使用匿名錶達式,則更進了一步,連方法名這些都簡化了。
3、Lambda表示式
匿名錶達式結構:
(param)->expression 或
(param)->{ sentences; }
函式式介面:
即只有一個抽象方法的介面(可以有多個預設方法)。
至於只有一個抽象方法,應該是為了便於繫結吧。畢竟匿名錶達式只能以引數和返回值來區分。
因此一個匿名錶達式可以繫結到多個函式式介面上,只要引數、返回值匹配。
java.util.function中定義了很多函式式介面,我們也能建立自己的函式式介面。
方法引用:
根據已有的方法來建立Lambda表示式。
這是在Lambda表示式中呼叫特定方法的一種便捷寫法。
方法引用說明了在建立的Lambda中執行哪個方法。
主要有三類:
1、指向靜態方法的方法引用
如 Integer::parseInt
指向構造器 ClassName::New(parameter)
2、指向類方法的方法引用
如 String::length
3、指向例項方法的方法引用
假設 a是一個物件,並且a是lambda表示式的一個引數,則可以使用方法引用來指向a的例項方法
(A a)->a.method; 或 (A a, B b)->a.mothod(b); 可改寫為:
a::method
@FunctionalInterface interface Wish { void handleWish(); static void defaultHandleWish() { System.out.println("defaultHandleWish!"); } default public void defaultHandleWishNonStatic() { System.out.println("defaultHandleWishNonStatic"); } } class Wish1 implements Wish{ Wish1(){ System.out.println("New!"); }; public static void handleWish1() { System.out.println("handleWish1"); } @Override public void handleWish() { System.out.println("handleWish2"); } } public class Ald { void wish(Wish a){ System.out.println("power activated!"); a.handleWish(); System.out.println("power deactivated!"); } public static void main(String[] args) { Ald ald=new Ald(); Wish1 wish1=new Wish1(); ald.wish(Wish1::handleWish1); //指向靜態方法,此時,a.handleWish();執行的是Wish1::handleWish1方法 ald.wish(Wish1::new); //指向構造器,此時,a.handleWish();執行的是Wish1的構造器方法 ald.wish(wish1::handleWish); //指向例項方法,此時,a.handleWish();執行的是wish1::handleWish方法 ald.wish(()-> System.out.println("handleWish3")); //lambda,此時,a.handleWish();執行的是System.out.println("handleWish3")語句 ald.wish(Wish::defaultHandleWish); //同樣Lambda中也能執行介面的static方法 ald.wish(wish1::defaultHandleWishNonStatic); //執行介面中的預設方法 } }
New!
power activated!
handleWish1
power deactivated!
power activated!
New!
power deactivated!
power activated!
handleWish2
power deactivated!
power activated!
handleWish3
power deactivated!
power activated!
defaultHandleWish!
power deactivated!
power activated!
defaultHandleWishNonStatic
power deactivated!