我上一篇文章介紹了函數(shù)式接口和Lambda表達(dá)式压储,以及Java解決所謂的閉包逝她。
這次深入一下技竟。
0x00 函數(shù)式接口
前面講了一下函數(shù)式接口,不過(guò)可能只是講了個(gè)大概勺拣,大致講了一下什么是函數(shù)式接口
- 函數(shù)式接口就是:一個(gè)interface奶赠,里面只有一個(gè)抽象方法,其他什么都沒(méi)有药有。
- FunctionalInterface注解標(biāo)注一個(gè)函數(shù)式接口毅戈,不能標(biāo)注
類
,方法
愤惰,枚舉
苇经,屬性
這些。 - 如果接口被標(biāo)注了
@FunctionalInterface
宦言,這個(gè)類就必須符合函數(shù)式接口的規(guī)范 - 即使一個(gè)接口沒(méi)有標(biāo)注
@FunctionalInterface
扇单,如果這個(gè)接口滿足函數(shù)式接口規(guī)則,依舊被當(dāng)作函數(shù)式接口奠旺。
這次我們來(lái)用代碼來(lái)深入了解函數(shù)式接口
如上圖蜘澜,只包含一個(gè)抽象方法是最普通的函數(shù)式接口
再看响疚,當(dāng)接口有兩個(gè)抽象方法的時(shí)候鄙信,就不在是函數(shù)式接口了,使用
@FunctionalInterface
標(biāo)注編譯時(shí)會(huì)報(bào)錯(cuò)
奇怪的是這里有3個(gè)抽象方法忿晕,為什么不報(bào)錯(cuò)装诡?
我們知道
toString
和equals
方法是Object的方法,Java基礎(chǔ)告訴我們践盼,Object是所有類的默認(rèn)父類鸦采,也就是說(shuō)任何對(duì)象都會(huì)包含Object里面的方法,即使是函數(shù)式接口的實(shí)現(xiàn)宏侍,也會(huì)有Object的默認(rèn)方法赖淤,所以:重寫(xiě)Object中的方法蜀漆,不會(huì)計(jì)入接口方法中,除了final不能重寫(xiě)的,Object中所能重寫(xiě)的方法搬素,寫(xiě)到接口中祈匙,不會(huì)影響函數(shù)式接口的特性
Java8 允許接口中含有非抽象方法,這種在接口中使用
default
修飾的非抽象方法稱為默認(rèn)方法鲜侥,默認(rèn)方法也不會(huì)影響函數(shù)式接口的特性褂始。我們依然可以認(rèn)為DemoConsumer
是一個(gè)函數(shù)式接口。
0x01 Lambda表達(dá)式深入
Lambda表達(dá)式的形式如下
(param1, param2, param3, param4…)->{ doing……}描函;
由此引申出多種寫(xiě)法:
//1.
() -> System.out.println("Hello Lambda");
//2.
number1 -> int a = number1 * 2;
//3.
(number1, number2) -> int a = number1 + number2;
//4.
(number1, number2) -> {
int a = number1 + number2;
System.out.println(a);
}
下面通過(guò)重構(gòu)一段代碼崎苗,來(lái)深入了解一下Lambda表達(dá)式
public class FunctionalInterfaceTest {
public static void main(String[] args) {
List<String> demoList = Arrays.asList( "Zing", "阿三", "小明", "小紅", "趙日天");
rollCall(demoList);
}
public static void rollCall(List<? extends String> list){
for(String name : list){
if(name.startsWith("小")){
System.out.println(name);
}
}
}
}
如果希望篩選的條件能自由定義狐粱,而不是name.startsWith("小")
寫(xiě)死,并且希望找到人名后胆数,不是簡(jiǎn)單的System.out.println(name);
肌蜻,而是能做一些其他的事情。
繼續(xù)重構(gòu):
/**
* 函數(shù)式接口
* @param <T>
*/
@FunctionalInterface
interface Checker<T extends String>{
boolean check(T t);
}
@FunctionalInterface
interface Out<T>{
void achievement(T t);
}
public class FunctionalInterfaceTest {
/**
* 點(diǎn)名
*/
@Test
public void testLambda() {
List<String> demoList = Arrays.asList("小明", "Zing", "阿三", "小紅", "趙日天");
rollCall(demoList,
name-> name.startsWith("Z"),
name->{
String rate = name + "是單身狗!";
System.out.println(rate);
});
}
/**
* 點(diǎn)名邏輯
* @param list
* @param checker
*/
public void rollCall(List<? extends String> list, Checker checker,Out out){
for(String name : list){
if(checker.check(name)){
out.achievement(name);
}
}
}
}
一不小心暴露了什么必尼。哈哈哈
通過(guò)上面的重構(gòu)蒋搜,很明顯,這么寫(xiě)也是合法的
Checker checker = name-> name.startsWith("Z"),
Out estimator = name->{
String rate = name + "是單身狗!";
System.out.println(rate);
};
由此可以知道判莉,Lambda和函數(shù)式接口是等價(jià)的豆挽。
0x02 補(bǔ)充
-
類型
有人會(huì)很奇怪,為什么Checker checker = name-> name.startsWith("Z")
這樣寫(xiě)的時(shí)候券盅,name會(huì)被當(dāng)成String 類型帮哈?
這是Java的類型推斷,大致邏輯是編譯器知道函數(shù)式接口方法的輸入?yún)?shù)類型锰镀,所以無(wú)論前面的參數(shù)是什么名字但汞,都會(huì)被當(dāng)成方法所需要的參數(shù)類型。
-
簡(jiǎn)單縮寫(xiě)
還有一個(gè)奇怪的地方name->name.startsWith("Z")
為什么這樣寫(xiě)也可以互站?
為什么不是寫(xiě)成``name->{ return name.startsWith("Z");}` 私蕾。
很明顯,后面的寫(xiě)法是沒(méi)有錯(cuò)的胡桃,
但是Idea會(huì)有一個(gè)虛線踩叭,說(shuō)明不需要寫(xiě)return
當(dāng)只需要執(zhí)行一條語(yǔ)句的時(shí)候,lambda支持這種簡(jiǎn)潔返回翠胰。所以為什么拒絕呢容贝?
-
外部參數(shù)
Lambda表達(dá)式是不能操作外部對(duì)象的,因?yàn)長(zhǎng)ambda 實(shí)質(zhì)上是接口的子對(duì)象之景,只能訪問(wèn)靜態(tài)資源和本身的內(nèi)部變量斤富。
編譯器會(huì)要求將外部變量使用
final
修飾锻狗。
-
和方法引用結(jié)合
方法引用Method References
是Java8配合Lambda一起做出的新特性满力,當(dāng)Lambda表達(dá)式里面只執(zhí)行已知的方法的時(shí)候,可以使用方法引用來(lái)寫(xiě)出跟簡(jiǎn)潔易讀的代碼
List<String> demoList = Arrays.asList("小明", "Zing", "阿三", "小紅", "趙日天");
demoList.forEach(System.out::println);
看到這里想必心里不禁想說(shuō)轻纪,我擦油额,好簡(jiǎn)潔!
官方給出了4種方法引用
Kinds of Method References
| Kind | Example|
| ---- |----|
|Reference to a static method|ContainingClass::staticMethodName|
|Reference to an instance method of a particular object|containingObject::instanceMethodName|
|Reference to an instance method of an arbitrary object of a particular type|ContainingType::methodName|
|Reference to a constructor| ClassName::new|
來(lái)源:
http://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html
我想我就不用翻譯了吧刻帚,出門(mén)百度翻譯??
love&peace
FS全棧計(jì)劃目錄:https://micorochio.github.io/fs-plan/