[TOC]java
其實我也無法說清楚他究竟是什麼鬼……程序員
就比如你沒見過某種顏色,我再怎麼描述都無法描述清楚,仍是親自看看他長啥樣吧app
還記得中學的時候,數學裏常常出現的那個符號嗎?ide
先放一個從百度圖庫偷來的圖片(CSDN打上的水印,不關我事哈……)函數
就是這貨,此處的lambda
就是它…… 測試
傳說中這玩意兒是比程序員還瘋狂的數學家的口頭禪……spa
上面的廢話都看完了????線程
雖然見到了他,可是仍是不清楚他究竟是什麼……scala
好了,不扯皮了,開講啦code
百科裏是這麼描述的:
Lambda 表達式」是一個匿名函數,能夠包含表達式和語句,而且可用於建立委託或表達式目錄樹類型。
看百科的描述好像有那麼點感受了。但是幹嗎非要搞個這麼高冷的解釋呢?
我的是這麼理解的:
lambda 相似於一個可調用的代碼塊或者遊離函數。固然也能夠有入參。
示例
好比下面這樣:
Comparator<Integer> c = (i, j) -> Integer.compare(i, j);
等價的原始Java代碼長這樣:
Comparator<Integer> c = new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return Integer.compare(o1, o2); } };
用上lambda是什麼體驗?
代碼量好像少了
逼格好像提升了
代碼好像更優雅了
至少在Java裏lambda的爸爸媽媽姐姐弟弟爺爺奶奶七大姑八大姨……可能都是長這個樣子的。
同時,據老夫多年的斷案經驗來推斷,lambda的本尊應該也是這個樣子的:
(Type1 param1,Type2 param2, ...) -> { // 一些亂七八糟、烏漆嘛黑的處理操做(); return ret; }
外觀一:沒有入參的時候
()->{return ret;}
外觀二:有參數的時候
(Type p)->{return ret;} // 參數類型能夠省略(隱式推掉) (p)->{return ret;} // 一個參數的時候,參數列表外的括號也能夠沒有 p->{return ret;}
外觀三:函數體只有一行的時候
// 方法體只有一行的時候,或括號連同結尾的分號均可以省略 e->ret
外觀四:沒有返回值的時候
e->{}
方法引用和構造器引用
instance::instanceMethod
className::staticMethod
className::instanceMethod
Arrays.asList(null, "", " ", "HelloWorld", "ByeBug")// .stream().filter(StringUtils::isNotBlank)// .forEach(System.out::println);
……
再看一個例子:
// 原始方法啓動一個線程 new Thread(new Runnable() { @Override public void run() { } }).start(); // lambda版 new Thread(()->{}).start();
不難看出,整個匿名內部類中最關鍵的代碼其實就是:
public void run() { }
因此,lambda中關鍵部分也就是這部分代碼了。
其實,用註解java.lang.FunctionalInterface
修飾的接口均可以用於lambda表達式中。
這種接口,都是隻有一個方法的接口。
另外,只要你的接口只有一個方法,即便是沒有@FunctionalInterface
註解修飾,也是能夠用lambda的(不少時候編譯器仍是很聰明的,他會自動推斷的)。
總之,lambda只鍾愛函數式接口(@FunctionalInterface
)。
public class HouseInfo { private Integer houseId; // 小區ID private String houseName;// 小區名 private Integer browseCount; // 瀏覽數 private Integer distance;// 距離 KM // constructor // getters // setters }
有以下數據
List<HouseInfo> houseInfos = Lists.newArrayList(// new HouseInfo(1, "恆大星級公寓", 100, 1), // new HouseInfo(2, "匯智湖畔", 999, 2), // new HouseInfo(3, "張江湯臣豪園", 100, 1), // new HouseInfo(4, "保利星苑", 23, 10), // new HouseInfo(5, "北顧小區", 66, 23), // new HouseInfo(6, "北傑公寓", null, 55), // new HouseInfo(7, "保利星苑", 77, 66), // new HouseInfo(8, "保利星苑", 111, 12)// );
按距離排序
Collections.sort(houseInfos, (h1, h2) -> { if (h1 == null || h2 == null) return 0; if (h1.getDistance() == null || h2.getDistance() == null) return 0; return h1.getDistance().compareTo(h2.getDistance()); });
輸出小區名
houseInfos.stream().map(h -> h.getHouseName()).forEach(System.out::println);
動態條件過濾
// 該函數式接口用來當作測試條件 public static interface Condition<T> { boolean conditionTest(T e); } // 定義一個方法來輸出知足condition測試結果的小區 public void printWithFilter(List<HouseInfo> houseList, Condition<HouseInfo> condition) { houseList.stream().filter(e -> condition.conditionTest(e)).forEach(System.out::println); } @Test public void test4() { List<HouseInfo> houseInfos = Lists.newArrayList(// new HouseInfo(1, "恆大星級公寓", 100, 1), // new HouseInfo(2, "匯智湖畔", 999, 2), // new HouseInfo(3, "張江湯臣豪園", 100, 1), // new HouseInfo(4, "保利星苑", 23, 10), // new HouseInfo(5, "北顧小區", 66, 23), // new HouseInfo(6, "北傑公寓", null, 55), // new HouseInfo(7, "保利星苑", 77, 66), // new HouseInfo(8, "保利星苑", 111, 12)// ); // 打印小區名包含北字的小區 printWithFilter(houseInfos, e -> e != null && e.getHouseName().contains("北")); // 打印距離大於10KM的小區 printWithFilter(houseInfos, e -> e != null && e.getDistance() != null && e.getDistance() > 10); }
字符串轉大寫
Arrays.asList("HelloWord", "ByeBug") .stream().map(String::toUpperCase).forEach(System.out::println);
其實在Scala裏lambda更加直觀
List("HelloWord", "ByeBug").map(_.toUpperCase()).foreach(println _);
經過上面的示例,再結合實際。不難發現最經常使用的lambda的形式以下:
單個輸入,無輸出
單個輸入,單個輸出
無輸入,輸出單個類型
兩個不一樣類型的輸入,第三種類型的輸出
兩個不一樣類型的輸入,其中一種類型的輸出
……
其實在每次使用的時候,不必本身去新建這些函數式接口以支持lambda,JDK就已經給lambda內置了不少好基友:
Consumer: 單個輸入,無輸出
public interface Consumer<T> { void accept(T t); }
BiConsumer: 兩個不一樣類型的輸入,無輸出
public interface BiConsumer<T, U> { void accept(T t, U u); }
Supplier: 無輸入,輸出單個類型
public interface Supplier<T> { T get(); }
Function: 兩個不一樣類型的輸入
public interface Function<T, R> { R apply(T t); // 輸入T類型,轉換成R類型 }
ToIntFunction / ToLongFunction / ToDoubleFunction
public interface ToIntFunction<T> { // T類型的輸入,輸出int // 相似於Stream的mapToInt() int applyAsInt(T value); }
IntFunction / LongFunction / DoubleFunction
public interface IntFunction<R> { R apply(int value); }
BiFunction: 兩個不一樣類型的輸入,第三種類型的輸出
public interface BiFunction<T, U, R> { R apply(T t, U u); }
條件測試
public interface Predicate<T> { boolean test(T t); }
能夠不用自定義函數式接口來支持lambda,上面的例子能夠改爲:
public void printWithFilter(List<HouseInfo> houseList, Predicate<HouseInfo> condition) { houseList.stream().filter(e -> condition.test(e)).forEach(System.out::println); }
關於lambda的傳說和他究竟是什麼鬼,看到這裏應該夠了。
畢竟咱們的主要目的是使用它,知道他怎麼用纔是重點。
不必糾結他嚴格的學術定義(這種事不該該是那種只會紙上談兵的老不死乾的嗎?)。
在Java8裏和lambda相關的主要API就是Stream
了。在瞭解Stream的時候來順便熟悉lambda吧。