Java8 學(xué)習(xí)筆記(一)——Lambda與Functional(函數(shù)式)接口

學(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é)果就是打印timesstring
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í) IWaterAnimalILandAnimal中都有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)省略的是啥,怎么省略。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容