學(xué)習(xí)是通過(guò)視頻和Java8新特性終極指南
1.Lambda
Lambda常見(jiàn)組成形式:參數(shù)列表——>函數(shù)體
匿名內(nèi)部類:
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello World!!!");
}
}).start();
Lambada形式:
new Thread(()->System.out.println("Hello World!!!")).start();
-
()包括的就是參數(shù)列表,但這里無(wú)參 -
System.out.println("Hello World!!!")是函數(shù)體
1.1 參數(shù)列表
- 沒(méi)有參數(shù)時(shí):一定要加上
()
new Thread(()->System.out.println("Hello World!!!")).start();
- 有一個(gè)參數(shù)時(shí):
1.如果寫(xiě)了參數(shù)類型,就需要加()
2.如果沒(méi)有寫(xiě)參數(shù)類型,可以不加()
Arrays.asList("qwe","asd","zxc").forEach((String s) -> System.out.println(s));
Arrays.asList("qwe","asd","zxc").forEach( s -> System.out.println(s));
- 有兩個(gè)參數(shù)時(shí):一定需要
();當(dāng)參數(shù)需要加修飾符或者標(biāo)簽時(shí),參數(shù)則需要加上完整的參數(shù)類型,否則,可以不加完整的參數(shù)類型。
Arrays.asList("b","a","c","d").sort((final String s1,final String s2)->s1.compareTo(s2));
1.2函數(shù)體
Lambda的函數(shù)體和普通Java的方法差不多
- 函數(shù)體只有一行,可以省略
{},需要返回值時(shí),return也可以省略
Arrays.asList("b","a","c","d").sort((s1,s2)->s1.compareTo(s2));
- 多行時(shí)則需要
{},需要返回值時(shí),return也不可以省略
Arrays.asList("b","a","c","d").sort((s1,s2) ->{
int result = s1.compareTo(s2);
return result;
});
1.3 Lambda表達(dá)式中的變量
- 參數(shù)
Arrays.asList("b","a","c","d").sort((s1,s2)->s1.compareTo(s2));
s1,s2就是Labmbda表達(dá)式的參數(shù)
- 局部變量和自由變量
private static void printHelloLambda(int times, String str) {
Runnable runnable = () -> {
for (int i = 0; i < times; i++) {
System.out.println(str);
}
};
new Thread(runnable).start();
}
運(yùn)行結(jié)果就是打印times次string
for()中的i就是局部變量
int times, String str既不是參數(shù),也不是局部變量,而是自由變量
自由變量在Lambda表達(dá)式中不能修改。如果是采用內(nèi)明內(nèi)部類的形式,內(nèi)部類想用使用方法的參數(shù)int times, String str,是需要加上final的。
在Lambda表達(dá)式中的this代表創(chuàng)建Lambda表達(dá)式方法的this,表達(dá)式所在的類。
Lambda表達(dá)式中的異常同普通方法一樣,也是有兩種處理方法。
1.表達(dá)式函數(shù)體內(nèi)進(jìn)行try-catch處理
2.接口方法進(jìn)行throws。Lambda所在的方法進(jìn)行throws是無(wú)效的。
1.4 Lambda表達(dá)式方法引用
Arrays.asList("b", "a", "c", "d").forEach(System.out :: println);
String[] strings = { "acb", "abc", "cb", "bc" };
List<String> list = Arrays.asList(strings);
Collections.sort(list, String::compareTo);
方法引用形式:
類 ::靜態(tài)方法
對(duì)象::方法
對(duì)象::靜態(tài)方法
1.5構(gòu)造方法引用
形式:類::new
Arrays.asList()返回的是ArrayList,如果我們想要可以根據(jù)需要來(lái)確定返回List的類型,就可以使用構(gòu)造方法引用。
import java.util.List;
@FunctionalInterface
public interface ICreater<T extends List<?>> {
T create();
}
定義一個(gè)接口泛型為List的子類,接口內(nèi)抽象方法無(wú)參,返回值為T
public class LambdaDemo {
public static void main(String[] args) {
forEach();
}
private static void forEach() {
LambdaDemo lambdaDemo = new LambdaDemo();
List<String> list_2 =lambdaDemo.asList(LinkedList::new, strings);
Collections.sort(list_2, String::compareTo);
list.stream().forEach(System.out::println);
}
public <T> List<T> asList(ICreater<List<T>> creater, T... t) {
List<T> list = creater.create();
for (T a : t)
list.add(a);
return list;
}
}
構(gòu)造方法引用,接口需要有一個(gè)無(wú)參的并且一定有返回值。
2.Functional(函數(shù)式)接口
語(yǔ)言設(shè)計(jì)者投入了大量精力來(lái)思考如何使現(xiàn)有的函數(shù)友好地支持lambda。最終采取的方法是:增加函數(shù)式接口的概念。函數(shù)式接口就是一個(gè)具有一個(gè)方法的普通接口。像這樣的接口,可以被隱式轉(zhuǎn)換為lambda表達(dá)式。java.lang.Runnable與java.util.concurrent.Callable是函數(shù)式接口最典型的兩個(gè)例子。在實(shí)際使用過(guò)程中,函數(shù)式接口是容易出錯(cuò)的:如有某個(gè)人在接口定義中增加了另一個(gè)方法,這時(shí),這個(gè)接口就不再是函數(shù)式的了,并且編譯過(guò)程也會(huì)失敗。為了克服函數(shù)式接口的這種脆弱性并且能夠明確聲明接口作為函數(shù)式接口的意圖,Java 8增加了一種特殊的注解@FunctionalInterface(Java 8中所有類庫(kù)的已有接口都添加了@FunctionalInterface注解)。
Functional接口就是只有一個(gè)抽象方法的接口。
注意是只有一個(gè)抽象方法,不是只有一個(gè)方法。也就是說(shuō)Functional接口除了一個(gè)抽象方法外,還可以有默認(rèn)方法和靜態(tài)方法。
2.1默認(rèn)方法
默認(rèn)方法就是在接口中定義一個(gè)方法用default修飾
定義一個(gè)水生生物接口IWaterAnimal:
@FunctionalInterface
public interface IWaterAnimal {
void run(String s);
default void breathe(){
System.out.println("可以在水中呼吸");
}
}
一個(gè)青蛙類Forg
public class Frog implements IWaterAnimal {
public static void main(String[] args) {
Forg forg = new Forg();
forg.breathe();
forg.run("我是青蛙,我會(huì)跳");
}
@Override
public void run(String s) {
System.out.println(s);
}
}
運(yùn)行結(jié)果就是:
可以在水中呼吸 我是青蛙,我會(huì)跳
2.1解決接口中默認(rèn)方法沖突
青蛙不僅可以在水中,還可以在陸地,再定義一個(gè)陸地動(dòng)物接口ILandAnimal
@FunctionalInterface
public interface ILandAnimal {
void run(String s);
default void breathe(){
System.out.println("可以在空氣呼吸");
}
}
青蛙類Frog再去實(shí)現(xiàn)ILandAnimal接口,這時(shí) IWaterAnimal和ILandAnimal中都有breathe()方法,此時(shí)的Forg類中則必須指明breathe()是實(shí)現(xiàn)的哪一個(gè)接口中的方法。
public class Frog implements IWaterAnimal,ILandAnimal{
public static void main(String[] args) {
Forg frog = new Forg();
forg.breathe();
forg.run("我是青蛙,我會(huì)跳");
}
@Override
public void run(String s) {
System.out.println(s);
}
@Override
public void breathe() {
ILandAnimal.super.breathe();
}
}
ILandAnimal.super.breathe();是指明了使用ILandAnimal接口中的breathe()方法,此時(shí)運(yùn)行結(jié)果就變得不一樣:
可以在空氣呼吸 我是青蛙,我會(huì)跳
如果指明了IWaterAnimal.super.breathe();,運(yùn)行結(jié)果就是:
可以在水中呼吸 我是青蛙,我會(huì)跳
當(dāng)然也可以選擇覆蓋這個(gè)breathe()方法,在Forg類中修改方法breathe()
@Override
public void breathe() {
System.out.println("青蛙既可以在水中呼吸也可以在空氣呼吸");
}
運(yùn)行結(jié)果就是:
青蛙既可以在水中呼吸也可以在空氣呼吸 我是青蛙,我會(huì)跳
當(dāng)實(shí)現(xiàn)的接口中默認(rèn)方法沖突時(shí),要通過(guò)接口名.super.方法名的方式來(lái)指定方法。
2.3父類方法與接口中默認(rèn)方法相同
青蛙有個(gè)大嘴巴,寫(xiě)一個(gè)BigMouth的類,然后Forg繼承這個(gè)BigMouth類
public class BigMouth {
public void openMouth(){
System.out.println("張開(kāi)大嘴巴");
}
}
此時(shí)的Forg類:
public class Forg extends BigMouth implements IWaterAnimal,ILandAnimal{
public static void main(String[] args) {
Forg frog = new Forg();
frog.breathe();
frog.run("我是青蛙,我會(huì)跳");
frog.openMouth();
}
@Override
public void run(String s) {
System.out.println(s);
}
@Override
public void breathe() {
System.out.println("青蛙既可以在水中呼吸也可以在空氣呼吸");
}
}
加入了第7行的方法后,運(yùn)行結(jié)果
青蛙既可以在水中呼吸也可以在空氣呼吸 我是青蛙,我會(huì)跳 張開(kāi)大嘴巴
這時(shí)在IWaterAnimal水生動(dòng)物接口中加入openMouth()方法
@FunctionalInterface
public interface IWaterAnimal {
void run(String s);
default void openMouth(){
System.out.println("水生生物張開(kāi)大嘴巴");
}
default void breathe(){
System.out.println("可以在水中呼吸");
}
}
運(yùn)行結(jié)果
青蛙既可以在水中呼吸也可以在空氣呼吸 我是青蛙,我會(huì)跳 張開(kāi)大嘴巴
根據(jù)運(yùn)行結(jié)果,IWaterAnimal接口中的openMouth()方法并沒(méi)有運(yùn)行。
當(dāng)父類中的方法和接口的默認(rèn)方法一樣時(shí),默認(rèn)調(diào)用的父類中的方法。也就是說(shuō)不要試圖通過(guò)接口的默認(rèn)方法來(lái)覆蓋Object類的方法。
--
2.4 靜態(tài)方法
@FunctionalInterface
public interface IWaterAnimal {
void run(String s);
static void swim(){
System.out.println("水生生物會(huì)游泳");
}
default void openMouth(){
System.out.println("水生生物張開(kāi)大嘴巴");
}
default void breathe(){
System.out.println("可以在水中呼吸");
}
}
在Forg類中就可以直接通過(guò)IWaterAnimal.swim()來(lái)調(diào)用。和普通的Java類的靜態(tài)方法相同。
3.最后
Lambda表達(dá)式簡(jiǎn)化了代碼的書(shū)寫(xiě),卻增加了閱讀代碼的難度。我現(xiàn)在剛剛開(kāi)始學(xué)使用,回頭看寫(xiě)的方法,往往還得思考寫(xiě)的是啥。現(xiàn)在學(xué)習(xí)寫(xiě)Lambda表達(dá)式的思路就是,代碼中哪些可以省略,省略后JVM能不能推測(cè)出來(lái)省略的是啥,怎么省略。