lambda表達式


一、Lambda表達式簡介

什么是Lambda?

Lambda是JAVA 8添加的新特性,說白了,Lambda是一個匿名函數

為什么使用Lambda?

使用Lambda表達式可以對一個接口的方法進行非常簡潔的實現

Lambda對接口的要求

雖然可以使用Lambda表達式對某些接口進行簡單的實現,但是並不是所有的接口都可以用Lambda表達式來實現,要求接口中定義的必須要實現的抽象方法只能是一個

在JAVA8中 ,對接口加了一個新特性:default,可以使用default對接口方法進行修飾,被修飾的方法在接口中可以默認實現。

@FunctionalInterface

修飾函數式接口的,接口中的抽象方法只有一個

二、Lambda的基礎語法

1. 語法

主要關注:(返回值類型、方法名)參數列表、方法體。

/**
* ():用來描述參數列表
*  {}:用來描述方法體 有時可以省略
*  ->: Lambda運算符 讀作goes to
*  例 Test t=()->{System.out.println("hello word")}; 大括號可省略
*/

2. 創建多個接口

/**
 * 無參數無返回值接口
 */
@FunctionalInterface
public interface LambdaNoneReturnNoneParmeter {
    void test();
}

/**
 * 無返回值有單個參數
 */
@FunctionalInterface
public interface LambdaNoneReturnSingleParmeter {
    void test(int n);
}

/**
 * 無返回值 多個參數的接口
 */
@FunctionalInterface
public interface LambdaNoneReturnMutipleParmeter {
    void test(int a,int b);
}

/**
 * 有返回值 無參數接口
 */
@FunctionalInterface
public interface LambdaSingleReturnNoneParmeter {
    int test();
}

/**
 * 有返回值 有單個參數的接口
 */
@FunctionalInterface
public interface LambdaSingleReturnSingleParmeter {
    int test(int n);
}

/**
 * 有返回值 有多個參數的接口
 */
@FunctionalInterface
public interface LambdaSingleReturnMutipleParmeter {
    int test(int a,int b);

3. 創建測試類

public class Syntax1 {

    public static void main(String[] args) {
        // 1.Lambda表達式的基礎語法
        // Lambda是一個匿名函數 一般關注的是以下兩個重點
        // 參數列表 方法體

        /**
         * ():用來描述參數列表
         *  {}:用來描述方法體
         *  ->: Lambda運算符 讀作goes to
         */

        // 無參無返回  
        LambdaNoneReturnNoneParmeter lambda1=()->{
            System.out.println("hello word");
        };
        lambda1.test();

        // 無返回值 單個參數 
        LambdaNoneReturnSingleParmeter lambda2=(int n)->{
            System.out.println("參數是:"+n);
        };
        lambda2.test(10);

        // 無返回值 多個參數
        LambdaNoneReturnMutipleParmeter lambda3=(int a,int b)->{
            System.out.println("參數和是:"+(a+b));
        };
        lambda3.test(10,12);

        // 有返回值 無參數
        LambdaSingleReturnNoneParmeter lambda4=()->{
            System.out.println("lambda4:");
            return 100;
        };
        int ret=lambda4.test();
        System.out.println("返回值是:"+ret);

        // 有返回值 單個參數
        LambdaSingleReturnSingleParmeter lambda5=(int a)->{
            return a*2;
        };
        int ret2= lambda5.test(3);
        System.out.println("單個參數,lambda5返回值是:"+ret2);

        //有返回值 多個參數
        LambdaSingleReturnMutipleParmeter lambda6=(int a,int b)->{
            return a+b;
        };
        int ret3=lambda6.test(12,14);
        System.out.println("多個參數,lambda6返回值是:"+ret3);
    }
}

輸出結果:
    hello word
    參數是:10
    參數和是:22
    lambda4:
    返回值是:100
    單個參數,lambda5返回值是:6
    多個參數,lambda6返回值是:26

三、語法精簡

1. 參數類型精簡

/**
* 語法精簡
* 1.參數類型
* 由於在接口的抽象方法中,已經定義了參數的數量類型 所以在Lambda表達式中參數的類型可以省略
* 備注:如果需要省略類型,則每一個參數的類型都要省略,千萬不要一個省略一個不省略
*/
LambdaNoneReturnMutipleParmeter lambda1=(int a,int b)-> {
    System.out.println("hello world"); 
};    
可以精簡為:
LambdaNoneReturnMutipleParmeter lambda1=(a,b)-> {
    System.out.println("hello world");
};

2. 參數小括號精簡

/**
* 2.參數小括號
* 如果參數列表中,參數的數量只有一個 此時小括號可以省略
*/
LambdaNoneReturnSingleParmeter lambda2=(a)->{
    System.out.println("hello world");
};
可以精簡為:
LambdaNoneReturnSingleParmeter lambda2= a->{
    System.out.println("hello world");
};

3. 方法大括號精簡

/**
* 3.方法大括號
* 如果方法體中只有一條語句,此時大括號可以省略
*/
LambdaNoneReturnSingleParmeter lambda3=a->{
    System.out.println("hello world");
};
可以精簡為:
LambdaNoneReturnSingleParmeter lambda3=a->System.out.println("hello world");

4. 大括號精簡補充

/**
* 4.如果方法體中唯一的一條語句是一個返回語句
* 賊省略大括號的同時 也必須省略return
*/
LambdaSingleReturnNoneParmeter lambda4=()->{
    return 10;
};
可以精簡為:
LambdaSingleReturnNoneParmeter lambda4=()->10;

5. 多參數,有返回值 精簡

LambdaSingleReturnNoneParmeter lambda5=(a,b)->{
    return a+b;
};
可以精簡為:
LambdaSingleReturnMutipleParmeter lambda5=(a,b)->a+b;

四、Lambda語法進階

1. 方法引用(普通方法和靜態方法)

在實際應用過程中,一個接口在很多地方都會調用同一個實現,例如:

LambdaSingleReturnMutipleParmeter lambda1=(a,b)->a+b;
LambdaSingleReturnMutipleParmeter lambda2=(a,b)->a+b;

這樣一來每次都要寫上具體的實現方法 a+b,如果需求變更,則每一處實現都需要更改,基於這種情況,可以將后續的是實現更改為已定義的 方法,需要時直接調用就行

/**
*方法引用:
* 可以快速的將一個Lambda表達式的實現指向一個已經實現的方法
* 方法的隸屬者 如果是靜態方法 隸屬的就是一個類  其他的話就是隸屬對象
* 語法:方法的隸屬者::方法名
* 注意:
*  1.引用的方法中,參數數量和類型一定要和接口中定義的方法一致
*  2.返回值的類型也一定要和接口中的方法一致
*/
public class Syntax3 {

    public static void main(String[] args) {
        
        LambdaSingleReturnSingleParmeter lambda1=a->a*2;
        LambdaSingleReturnSingleParmeter lambda2=a->a*2;
        LambdaSingleReturnSingleParmeter lambda3=a->a*2;
        //簡化
        LambdaSingleReturnSingleParmeter lambda4=a->change(a);
        //方法引用
        LambdaSingleReturnSingleParmeter lambda5=Syntax3::change;
    }

    /**
    * 自定義的實現方法
    */
    private static int change(int a){
        return a*2;
    }
}

2. 方法引用(構造方法)

//實體類
public class Person {
    public String name;
    public int age;

    public Person() {
        System.out.println("Person的無參構造方法執行");
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("Person的有參構造方法執行");
    }
}

//需求:兩個接口,各有一個方法,一個接口的方法需要引用Person的無參構造,一個接口的方法需要引用Person的有參構造 用於返回兩個Person對象,例:
interface PersonCreater{
    //通過Person的無參構造實現
    Person getPerson();
}

interface PersonCreater2{
    //通過Person的有參構造實現
    Person getPerson(String name,int age);
}

//那么可以寫作
public class Syntax4 {
    public static void main(String[] args) {

        PersonCreater creater=()->new Person();

        //引用的是Person的無參構造
         //PersonCreater接口的方法指向的是Person的方法
        PersonCreater creater1=Person::new; //等價於上面的()->new Person()
        //實際調用的是Person的無參構造 相當於把接口里的getPerson()重寫成new Person()。
        Person a=creater1.getPerson(); 

        //引用的是Person的有參構造
        PersonCreater2 creater2=Person::new;
        Person b=creater2.getPerson("張三",18);
    }
}

//注意:是引用無參構造還是引用有參構造 在於接口定義的方法參數

五、綜合練習

1. 集合排序案例

public class Exercise1 {

    public static void main(String[] args) {

        //需求:已知在一個ArrayList中有若干各Person對象,將這些Person對象按照年齡進行降序排列
        ArrayList<Person> list=new ArrayList<>();


        list.add(new Person("張三",10));
        list.add(new Person("李四",12));
        list.add(new Person("王五",13));
        list.add(new Person("趙六",14));
        list.add(new Person("李雷",11));
        list.add(new Person("韓梅梅",8));
        list.add(new Person("jack",10));

        System.out.println("排序前:"+list);

        //將排列的依據傳入 具體的方法指向的是 內部元素的age相減 sort會依據結果的正負進行降序排列
        //sort 使用提供的 Comparator對此列表進行排序以比較元素。
        list.sort((o1, o2) -> o2.age-o1.age);

        System.out.println("排序后:"+list);
    }
}

2. TreeSet排序案例

public class Exercise2 {
    public static void main(String[] args) {

        /**Treeset 自帶排序
         * 但是現在不知道Person誰大誰小無法排序
         * 解決方法:
         * 使用Lambda表達式實現Comparator接口,並實例化一個TreeSet對象
         * 注意:在TreeSet中如果Comparator返回值是 0 會判斷這是兩個元素是相同的 會進行去重
         * TreeSet<Person> set=new TreeSet<>((o1, o2) -> o2.age-o1.age); 
         * 這個獲取的對象打印會少一個Person
         * 此時我們將方法修改
        */
        TreeSet<Person> set=new TreeSet<>((o1, o2) ->{
            if(o1.age>=o2.age){
                return -1;
            }else {
                return 1;
            }
        });

        set.add(new Person("張三",10));
        set.add(new Person("李四",12));
        set.add(new Person("王五",13));
        set.add(new Person("趙六",14));
        set.add(new Person("李雷",11));
        set.add(new Person("韓梅梅",8));
        set.add(new Person("jack",10));

        System.out.println(set);
    }
}

3. 集合的遍歷

public class Exercise3 {

    public static void main(String[] args) {
        ArrayList<Integer> list=new ArrayList<>();

        Collections.addAll(list,1,2,3,4,5,6,7,8,9);
        /**
         * list.forEach(Consumer<? super E> action) 
         * api文檔解釋: 對 集合中的每個元素執行給定的操作,直到所有元素都被處理或動作引發異常。
         * 將集合中的每一個元素都帶入到接口Consumer的方法accept中  然后方法accept指向我們的引用
         * 輸出集合中的所有元素
         * list.forEach(System.out::println);
        */

        //輸出集合中所有的偶數
        list.forEach(ele->{
            if(ele%2==0){
                System.out.println(ele);
            }
        });
    }
}

4. 刪除集合中滿足條件的元素

public class Exercise4 {

    public static void main(String[] args) {
        ArrayList<Person> list=new ArrayList<>();

        list.add(new Person("張三",10));
        list.add(new Person("李四",12));
        list.add(new Person("王五",13));
        list.add(new Person("趙六",14));
        list.add(new Person("李雷",11));
        list.add(new Person("韓梅梅",8));
        list.add(new Person("jack",10));

        //刪除集合中年齡大於12的元素
        /**
         * 之前迭代器的做法
         * ListIterator<Person> it = list.listIterator();
         * while (it.hasNext()){
         *   Person ele=it.next();
         *   if(ele.age>12){
         *         it.remove();
         *   }
         * }
         */

        /**
         * lambda實現
         * 邏輯
         * 將集合中的每一個元素都帶入到接口Predicate的test方法中,
         * 如果返回值是true,則刪除這個元素
        */
        list.removeIf(ele->ele.age>10);
        System.out.println(list);
    }
}

5. 開辟一條線程 做一個數字的輸出

public class Exercise5 {
    public static void main(String[] args) {

        /**
         * 通過Runnable 來實例化線程
         */
        Thread t=new Thread(()->{
            for(int i=0;i<100;i++){
                System.out.println(i);
            }
        });
        t.start();
    }
}

六、系統內置的函數式接口

public class FunctionalInterface {
    public static void main(String[] args) {

        // Predicate<T>              :     參數是T 返回值boolean  
        // 在后續如果一個接口需要指定類型的參數,返回boolean時可以指向 Predicate
        //          IntPredicate            int -> boolean
        //          LongPredicate           long -> boolean
        //          DoublePredicate         double -> boolean

        // Consumer<T>               :      參數是T 無返回值(void)
        //          IntConsumer             int ->void
        //          LongConsumer            long ->void
        //          DoubleConsumer          double ->void

        // Function<T,R>             :      參數類型T  返回值R
        //          IntFunction<R>          int -> R
        //          LongFunction<R>         long -> R
        //          DoubleFunction<R>       double -> R
        //          IntToLongFunction       int -> long
        //          IntToDoubleFunction     int -> double
        //          LongToIntFunction       long -> int
        //          LongToDoubleFunction    long -> double
        //          DoubleToLongFunction    double -> long
        //          DoubleToIntFunction     double -> int

        // Supplier<T> : 參數 無 返回值T
        // UnaryOperator<T> :參數T 返回值 T
        // BiFunction<T,U,R> : 參數 T、U 返回值 R
        // BinaryOperator<T> :參數 T、T 返回值 T
        // BiPredicate<T,U> :  參數T、U  返回值 boolean
        // BiConsumer<T,U> :    參數T、U 無返回值

        /**
         * 常用的 函數式接口
         * Predicate<T>、Consumer<T>、Function<T,R>、Supplier<T>
         */
    }
}

七、Lambda閉包

public class ClosureDemo {
    public static void main(String[] args) {

        /**
         * lambda的閉包會提升包圍變量的生命周期
         * 所以局部變量 num在getNumber()方法內被 get()引用 不會在getNumber()方法執行后銷毀
         * 這種方法可以在外部獲取到某一個方法的局部變量
         */
        int n=getNumber().get();
        System.out.println(n);
    }
    private static Supplier<Integer> getNumber(){
        int num=10;
        /**
         * Supplier supplier=()->num;
         * return supplier;
         */
        return ()->{
            return num;
        };
    }
}

public class ClosureDemo2 {
    public static void main(String[] args) {
        int a=10;
        Consumer<Integer> c=ele->{
            System.out.println(a+1);
            //System.out.println(ele);
            //System.out.println(a++); 會報錯
            //在lambda中引用局部變量 這個變量必須是一個常量
        };
        //a++; 這樣也會導致內部報錯
        //如果在內部已經引用局部變量 參數傳遞后 打印的還是 10
        c.accept(1);
    }
}

 

List<Map<String,Object>> treeList = reportservice.getClassificationTreeNode("****");
treeList.stream().filter(item -> item.get("parentId").equals("****")).collect(Collectors.toList());

注意:本篇文章內容來自於https://www.bilibili.com/video/av52431330/?  僅用於個人學習和總結,如有侵權,聯系刪除。

持續更新!!!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM