【Java進階】01 Lambda 使用與進階

Lambda 表達式


初識 Lambda


  • 什麼是 Lambda 表達式?java

    • 自 Java 8 增長的新特性
    • 一個匿名方法(一個沒有名字的方法,在定義變量的時候定義的方法)
  • 爲何使用 Lambda 算法

    • 簡潔實現接口對接;閉包

    • 雖然可使用 Lambda 表達式對某些接口進行實現,可是並非全部的接口均可以使用 Lambda 來實現;ide

    • 實現要求:要實現的抽象方法只能是一個;函數

    • 在 Java 8 中對接口實現了一個新特性 default ,被此方法修飾的方法在類中有一個默認實現,因此不影響使用 Lambda 的實現;優化

  • @FunctionalInterfacethis

    • 一個用來修飾函數式接口的,接口中的抽象方法只能有一個;.net

    • // 錯誤代碼展現
      @FunctionalInterface
      interface Comparer{
          int compare(int a,int b);// 方法一
          int show(int a,int b);	 // 方法二
      }

      ↑ ↑ ↑ 錯誤代碼展現 ↑ ↑ ↑線程

    • 什麼是函數式接口?code

  • 代碼示例

public class DEMO{
   public static void main(String[] args){
       // 方法一:使用接口實現類
       Comparator comparator = new MyComparator();
       
       //方法二:使用匿名內部類
       Comparator comparator = new Comparator(){
           @Override
           public int compare(int a,int b){
               return a-b;
           }
       }
       
       //方法三:使用Lambda表達式
       Comparator comparator2 = (a,b) -> a-b;
   }
}

// 一個接口
interface Comparator{
   int compare(int a,int b);
}

//方法一:使用實現類實現接口
class MyComparator implements Comparator{
   @Override
   public int compare(int a,int b){
       return a-b;
   }
}

Lambda 基礎語法


  • 基礎語法:

    • Lambda 是一個匿名函數
    • 包含:參數列表、方法體、(不包含:返回值類型、方法名)
  • 元素:

    • ():用來描述參數列表
    • {}:用來描述方法體
    • ->:分隔 Lambda 運算法,讀做:goes to
  • 幾種狀況:

一、無參  無返回

// 無參 無返回
LambdaTest lam1 = ()->{
 Sout("Hello World!");
}
lam1.test();
// 結果:輸出`Hello World!`

二、單個參 無返回

// 無參 單個返回值
LambdaTest lam2 = (int a)->{
 Sout(a);
}
lam2.test(10);
// 結果:輸出`10`

三、多個參 無返回

LambdaTest lam3 = (int a,int b)->{
 Sout(a+b);
}
lam3.test(10,11);
// 結果:輸出`21`

四、無參 有返回

LambdaTest lam4 = ()->{
 Sout(100);
 return 100;
}
int ret = lam4.test();
// 結果:輸出`100` ; 返回 `100`

五、有參數 有返回

LambdaTest lam5 = (int a)->{
 Sout(a*2);
 return a*2;
}
int ret = lam5.test(10);
// 結果:輸出`20` ; 返回 `20`

↑ ↑ ↑ 要注意 必定要定義相應的 函數式接口 ↑ ↑ ↑


Lambda 語法精簡


  • 錯誤代碼演示:
interface Lamx{
    int test(int a,int b);
}

Lamx lamx = (int a,int b)->{
    Sout("a=" + a + "\nb=" + b);
    // return a+b;
}

上面報錯了?Lambda表達式如何知道有返回值?

Lambda 由於定義的接口有了說明,好比接口中寫int test(int a);第一個int表示返回值類型,第二個int 表示參數類型,因此 捨去了 return 就會報錯;

既然在定義接口的時候已經定義了類型,那麼是否是能夠在寫入參數的時候,將參數類型描述給省略掉呢?

  • 精簡1:省略參數描述int

    代碼演示:

interface Lamx{
    int test(int a,int b);
}

Lamx lamx = (a,b)->{
    Sout("a=" + a + "\nb=" + b);
    return a+b;
}

接口中已經說明了有int類型的返回值,因此不寫return是錯誤的;

兩個必須同時省略,不能一個寫一個不寫;

  • 精簡2:省略小括號()

    代碼演示:

interface Lamx{
    int test(int a);
}

Lamx lamx = a -> {
    Sout("a=" + a );
    return a;
}

當 入參 只有一個的時候,能夠省略入參的 括號;

  • 精簡3:省略大括號{}
interface Lamx{
    int test(int a);
}

Lamx lamx = a -> Sout( a );

當 函數 只有一行的時候,能夠省略大括號;

  • 精簡4:省略return
interface Lamx{
    int test(int a);
}

Lamx lamx = a -> a;

若是惟一一條語句是return語句,那麼省略大括號的同時,也必須省略return

Lamx lamx = (a,b)->a+b;
lamx.test(5,10);

Lambda 語法進階


  • Lambda 調用靜態方法
    • 代碼示例:
public class Test{
    public static void main(String[] args){
        // 實現方式一: 傳統 Lambda 語法
        Lamx lamx = a -> change(a);
        // 實現方式二: 雙" : "語法
        Lamx lamx = Test::change;
    }
    
    public static int change(int a){
        return a*2;
    }
}

將 Lambda 指向一個已經實現的方法;語法規則 -- 方法隸屬者::方法名

此處的change是一個靜態方法,隸屬者就是Test類,因此就是Test::change

  • 注意事項:
    • 參數的數量和類型必定要和方法中定義的一致
    • 返回型必定要和接口中定義的方法一致

  • Lambda 調用構造方法
    • 保留
// 構造方法
public class Person{
    public String name;
    public int age;
    
    public Person(){
        Sout("Person類的 無參 構造方法執行了;");
    }
    
    public Person(String name,int age){
        this.name = name;
        this.age  = age;
        Sout("Person類的 有參 構造方法執行了;");
    }
}
// Lambda 語法
public class Lamx{
    public static void main(String[] args){
        // 原始方法
        PersonCreater creater1 = () -> {
            return new Person();
        };
        
        // 簡化方法
        PersonCreater creater2 = () -> new Person();
        
        // 構造方法的引用
        PersonCreater creater3 = Person::new;
        Person a = creater3.getPersonNull();
        Person a = creater3.getPersonDouble("張三",18);
    }
}

//接口中要說明 返回值類型 方法參數!!!
interface PersonCreater{
    Person gerPersonNull();
    Person getPersonDouble(String name,int age);
}

實現語法 -- 類::new


Lambda 實現實例


一、已知在一個 ArrayList 類中有若干個 Person 對象,講這些Person對象按照年齡進行降序排序;

public class Exercise{
    public static void main(String[] args){
        ArrayList<Person> list = new ArrayList<>();
        list.add(new Person("張三",12));
        list.add(new Person("李四",13));
        list.add(new Person("韓梅梅",16));
        list.add(new Person("趙四",6));
        list.add(new Person("尼古拉斯凱奇",46));
        list.add(new Person("Tony",54));
        
        list.sort((o1,o2)-> o2.age-o1.age);
    }
}

二、使用TreeSet排序

TreeSer<Person> set = new TreeSer<>((o1,o2)->o2.age-01.age);
set.add(new Person("xiaoming",10));
set.add(new Person("wanggang",11));
set.add(new Person("zhaosi",8));
set.add(new Person("Nigu",43));
set.add(new Person("Tony",22));
set.add(new Person("poily",8));
set.add(new Person("Wngffei",40));
Sout(set);
//運行結果:基本正確,可是隻有一個8歲的人;

代碼優化

TreeSer<Person> set = new TreeSer<>((o1,o2)->{
    if(o1.age >= o2.age){
        retrun -1;
    }else{
        return 1;
    }
});
set.add(new Person("xiaoming",10));
set.add(new Person("wanggang",11));
set.add(new Person("zhaosi",8));
set.add(new Person("Nigu",43));
set.add(new Person("Tony",22));
set.add(new Person("poily",8));
set.add(new Person("Wngffei",40));
Sout(set);
//運行結果:正確;

public static void main(String[] args){
    ArrayList<Interger> list -= new ArrayList<>();
    Collection.addAll(list,1,2,3,4,5,6,7,8,9,0);
    list.forEach(ele->{
        if(ele%2 == 0)
            Sout(ele);
    });
}

使用 Lambda 實現線程的實例化

實現線程的實例化;


public static void main(String[] args){
    Thread t = new Thread(()->{
        for(int i=0;i<100;i++){
            Sout(i);
        }
    });
    t.start();
}

系統內置的函數式接口


public static void (String[] args){、
    //最經常使用的是Predicate、Consumer、Function、Supplier
    
    // Predicate<T>		:	參數T ·返回值 boolean
    //	IntPredicate	:	int-> boolean
    //	LongPredicate	:	long->boolean
    //	DoublePredicate	:	double
    
    // Consumer<T>		:	參數T ·返回值 void
    // 	IntConsumer		:	int ->void
    // 	LongConsumer
    // 	DoubleConsumer
    
    // Function<T,R>	:	參數T ·返回值 R
    //	IntFuncation	:	int->R
    //	LongFunction
    //	DoubleFunction
    //	IntToLongFunction:	int->long
    //	IntToDoubleFunction
    //	LongToIntFunction
    //	LongToDoubleFunction
    //	DoubleToIntFunction
    //	DoubleToLongFunction
    
    // Supplier<T>		:	參數無 ·返回值 T
 	//	……省略好多好多   
    // UnaryOperator<T>	:	參數T	 ·返回值T
    // BinaryOperator	:	參數T,T ·返回值T
    // BiFunction<T,U,R>:	參數T,U ·返回值R
    // BiPredicate<T,U>	:	參數T,U ·返回值boolean
    // BiConsumer<T,U>	:	參數T,U ·返回值void
}

閉包問題


  • 什麼是閉包
    • 閉包會提高變量的生命週期,這樣就能夠獲取某一個方法中的局部變量
public static void main(String[] args){
    int n = getNum().get();
    Sout(n);
    //結果是10;
}

private static Supplier<Integer> getNum(){
    int num = 10;
    
    return ()->{
        return num;
    };
}

代碼二:

PSVM(String[] args){//public static void main
    int a = 10;
    
    Consumer<Integer> c = ele->{
        Sout(a+1);
    };
    
    c.accept(1);// 結果11
    
        Consumer<Integer> c = ele->{
        Sout(a++);
    };
    
    c.accept();// 結果報錯,由於 閉包 中的變量必須是 final 的(也就是一個常量),因此不能修改這個值
}
相關文章
相關標籤/搜索