[TOC]
0 lambda的傳說
其實我也沒法說清楚他到底是什么鬼……
就好比你沒見過某種顏色富蓄,我再怎么描述都沒法描述清楚官边,還是親自看看他長啥樣吧
還記得中學(xué)的時候谁鳍,數(shù)學(xué)里經(jīng)常出現(xiàn)的那個符號嗎?
先放一個從百度圖庫偷來的圖片(CSDN打上的水印歼跟,不關(guān)我事哈……)
就是這貨轩性,此處的lambda
就是它……
傳說中這玩意兒是比程序員還瘋狂的數(shù)學(xué)家的口頭禪……
上面的廢話都看完了声登??揣苏?悯嗓?
雖然見到了他,但是還是不清楚他到底是什么……
好了舒岸,不扯皮了绅作,開講啦
- 百科里是這么描述的:
Lambda 表達(dá)式”是一個匿名函數(shù),可以包含表達(dá)式和語句蛾派,并且可用于創(chuàng)建委托或表達(dá)式目錄樹類型俄认。
看百科的描述好像有那么點感覺了『檎В可是干嘛非要搞個這么高冷的解釋呢眯杏?
- 個人是這么理解的:
lambda 類似于一個可調(diào)用的代碼塊或者游離函數(shù)。當(dāng)然也可以有入?yún)ⅰ?/p>
1 瞄一眼他長啥樣?
- 示例
比如下面這樣:
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是什么體驗壳澳?
- 代碼量好像少了
- 逼格好像提高了
- 代碼好像更優(yōu)雅了
2 lambda各種外觀
至少在Java里lambda的爸爸媽媽姐姐弟弟爺爺奶奶七大姑八大姨……可能都是長這個樣子的岂贩。
同時,據(jù)老夫多年的斷案經(jīng)驗來推斷巷波,lambda的本尊應(yīng)該也是這個樣子的:
(Type1 param1,Type2 param2, ...) -> {
// 一些亂七八糟萎津、烏漆嘛黑的處理操作();
return ret;
}
- 外觀一:沒有入?yún)⒌臅r候
()->{return ret;}
- 外觀二:有參數(shù)的時候
(Type p)->{return ret;}
// 參數(shù)類型可以省略(隱式推掉)
(p)->{return ret;}
// 一個參數(shù)的時候卸伞,參數(shù)列表外的括號也可以沒有
p->{return ret;}
- 外觀三:函數(shù)體只有一行的時候
// 方法體只有一行的時候,或括號連同結(jié)尾的分號都可以省略
e->ret
- 外觀四:沒有返回值的時候
e->{}
- 方法引用和構(gòu)造器引用
- instance::instanceMethod
- className::staticMethod
- className::instanceMethod
Arrays.asList(null, "", " ", "HelloWorld", "ByeBug")//
.stream().filter(StringUtils::isNotBlank)//
.forEach(System.out::println);
- ……
3 lambda使用場景
再看一個例子:
// 原始方法啟動一個線程
new Thread(new Runnable() {
@Override
public void run() {
}
}).start();
// lambda版
new Thread(()->{}).start();
不難看出,整個匿名內(nèi)部類中最關(guān)鍵的代碼其實就是:
public void run() {
}
所以锉屈,lambda中關(guān)鍵部分也就是這部分代碼了荤傲。
其實,用注解java.lang.FunctionalInterface
修飾的接口都可以用于lambda表達(dá)式中颈渊。
這種接口遂黍,都是只有一個方法的接口。
另外俊嗽,只要你的接口只有一個方法雾家,即使是沒有@FunctionalInterface
注解修飾,也是可以用lambda的(很多時候編譯器還是很聰明的,他會自動推斷的)。
總之绍豁,lambda只鐘愛函數(shù)式接口(@FunctionalInterface
)芯咧。
4 再來個示例
public class HouseInfo {
private Integer houseId; // 小區(qū)ID
private String houseName;// 小區(qū)名
private Integer browseCount; // 瀏覽數(shù)
private Integer distance;// 距離 KM
// constructor
// getters
// setters
}
- 有如下數(shù)據(jù)
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, "北顧小區(qū)", 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());
});
- 輸出小區(qū)名
houseInfos.stream().map(h -> h.getHouseName()).forEach(System.out::println);
- 動態(tài)條件過濾
// 該函數(shù)式接口用來當(dāng)做測試條件
public static interface Condition<T> {
boolean conditionTest(T e);
}
// 定義一個方法來輸出滿足condition測試結(jié)果的小區(qū)
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, "北顧小區(qū)", 66, 23), //
new HouseInfo(6, "北杰公寓", null, 55), //
new HouseInfo(7, "保利星苑", 77, 66), //
new HouseInfo(8, "保利星苑", 111, 12)//
);
// 打印小區(qū)名包含北字的小區(qū)
printWithFilter(houseInfos, e -> e != null && e.getHouseName().contains("北"));
// 打印距離大于10KM的小區(qū)
printWithFilter(houseInfos, e -> e != null && e.getDistance() != null && e.getDistance() > 10);
}
- 字符串轉(zhuǎn)大寫
Arrays.asList("HelloWord", "ByeBug")
.stream().map(String::toUpperCase).forEach(System.out::println);
- 其實在Scala里lambda更加直觀
List("HelloWord", "ByeBug").map(_.toUpperCase()).foreach(println _);
5 lambda的好基友
通過上面的示例,再結(jié)合實際妹田。不難發(fā)現(xiàn)最常用的lambda的形式如下:
- 單個輸入唬党,無輸出
- 單個輸入鹃共,單個輸出
- 無輸入鬼佣,輸出單個類型
- 兩個不同類型的輸入,第三種類型的輸出
- 兩個不同類型的輸入霜浴,其中一種類型的輸出
- ……
其實在每次使用的時候晶衷,沒必要自己去新建這些函數(shù)式接口以支持lambda,JDK就已經(jīng)給lambda內(nèi)置了很多好基友:
- 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類型,轉(zhuǎn)換成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);
}
可以不用自定義函數(shù)式接口來支持lambda锹漱,上面的例子可以改成:
public void printWithFilter(List<HouseInfo> houseList, Predicate<HouseInfo> condition) {
houseList.stream().filter(e -> condition.test(e)).forEach(System.out::println);
}
關(guān)于lambda的傳說和他到底是什么鬼,看到這里應(yīng)該夠了慕嚷。
畢竟我們的主要目的是使用它哥牍,知道他怎么用才是重點。
沒必要糾結(jié)他嚴(yán)格的學(xué)術(shù)定義(這種事不應(yīng)該是那種只會紙上談兵的老不死干的嗎喝检?)嗅辣。
在Java8里和lambda相關(guān)的主要API就是Stream
了。在了解Stream的時候來順便熟悉lambda吧挠说。