JDK新特性-Lambda表示式的神操作

牧小農發表於2020-11-07

一、Lambda表示式的介紹

  • Lambda表示式是 Java8 中最重要的新功能之一。使用 Lambda 表達
    式可以替代只有一個抽象函式的介面實現,告別匿名內部類,程式碼看
    起來更簡潔易懂。Lambda表示式同時還提升了對集合、框架的迭代、
    遍歷、過濾資料的操作。

  • lambda表示式可以替代只有一個抽象函式的介面實現,告別匿名內部類,程式碼看起來更簡潔易懂

  • lambda表示式同時還提升了對集合、框架的迭代、遍歷、過濾資料的操作

  • lambda可以極大的減少程式碼冗餘,同時程式碼的可讀性要好過冗長的內部類,匿名類

例如以前我們使用匿名內部類來實現程式碼:

        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("running1 .....");
            }
        };
        runnable.run();

使用lambda表示式實現更簡潔的程式碼:

        Runnable runnable3 = ()-> System.out.println("running2....");
        runnable3.run();

lambda表示式語法:

LambdaParameters -> LambdaBody

在這裡插入圖片描述
args -> expr或者(object ... args)-> {函式式介面抽象方法實現邏輯}

​ 1、()引數的個數,根據函式式介面裡面抽象的引數個數來決定,當引數只有一個的時候,()可以省略

​ 2、當expr邏輯非常簡單的時候,{}和return可以省略

案例說明:

    public static void main(String[] args) throws Exception {
		Callable<String> c1 = new Callable() {
            @Override
            public String call() throws Exception {
                return "muxiaonong";
            }
        };
        System.out.println(c1.call());

        Callable<String> c2 = ()->{return "muxiaonong2";};
        System.out.println(c2.call());

		//邏輯很簡單的時候省略 {} 和 return
        Callable<String> c3 = ()->"muxiaonong3";
        System.out.println(c3.call());
	}

二、Lambda表示式的特點

  • 函數語言程式設計
  • 引數型別自動推斷
  • 程式碼量少,簡潔

三、Lambda表示式案例

實現方式列表:

​	    ()->{}
​		()->{System.out.println(1);}
​		()->System.out.println(1)
​		()->{return 100;}
​		()->100
​		()->null
​		(int x)->{return x+1;}
​		(int x)->x+1
​		(x)->x+1
​		x->x+1

案例1:執行緒實現方式:

    public static void main(String[] args) {
        //匿名內部類方式
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("runing1..........");
            }
        });
        
        //Lambda表示式方式
        new Thread(() -> {System.out.println("runing2.....");}).start();
    }

案例2:集合遍歷實現方式

    public static void main(String[] args) {
        List<String> list = Arrays.asList("java","python","scala","javascript");

        //普通匿名內部類方式
        Collections.sort(list, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.length() - o2.length();
            }
        });

        //Lambda方式
        Collections.sort(list,(a,b) -> a.length() - b.length());
        list.forEach(System.out::println);
    }

四、Lambda表示式的應用場景

重要的事情說三遍:任何有函式式介面的地方 * 3

什麼是函式式介面: 只有一個抽象方法(Object類中的方法除外)的介面是函式式介面

五、Lambda表示式實際應用

5.1 無參實體類模擬

模擬資料庫連線層:

@FunctionalInterface
public interface StudentDao {
    void insert(Student student);
}

實體類

/** @Author mxn
 * @Description 學生實體類
 * @Date 10:19 2020/11/7
 * @Param 
 * @return 
 **/
public class Student {
}

 public static void main(String[] args) {
        StudentDao sd1 = new StudentDao() {
            @Override
            public void insert(Student student) {
                System.out.println("插入學生1");
            }
        };

        StudentDao sd2 = (student)->{
            System.out.println("student: "+student);
        };

        StudentDao sd3 = (Student student)-> System.out.println("student3:"+student);

        sd1.insert(new Student()); //輸出 插入學生1
        sd2.insert(new Student());// 輸出
        sd3.insert(new Student());// 輸出
    }

5.2 有參實體類模擬

實體類

/** @Author mxn
 * @Description 
 * @Date 10:26 2020/11/7
 * @Param
 * @return
 **/
public class Teacher {
}

介面模擬層

@FunctionalInterface
public interface TeacherDao {
    int get(Teacher teacher);
}

實現層

 public static void main(String[] args) {
        TeacherDao td1 = new TeacherDao() {
            @Override
            public int get(Teacher teacher) {
                return 1;
            }
        };
        TeacherDao td2 = (teacher)->{return 2;};
        TeacherDao td3 = (Teacher teacher)->{return 3;};
        TeacherDao td4 = (teacher)->4;
        TeacherDao td5 = (Teacher teacher)->5;

        System.out.println(td1.get(new Teacher()));//輸出 1
        System.out.println(td2.get(new Teacher()));//輸出 2
        System.out.println(td3.get(new Teacher()));//輸出 3
        System.out.println(td4.get(new Teacher()));//輸出 4
        System.out.println(td5.get(new Teacher()));//輸出 5
}

六、函式式介面

Supplier:代表一個輸出
Consumer:代表一個輸入
BiConsumer:代表兩個輸入
Function:代表一個輸入,一個輸出(一般輸入和輸出是不同型別的)
UnaryOperator:代表一個輸入,一個輸出(輸入和輸出是相同型別的)
BiFunction:代表兩個輸入,一個輸出(一般輸入和輸出是不同型別的)
BinaryOperator:代表兩個輸入,一個輸出(輸入和輸出是相同型別的)

在Java中提供了一系列的函式式介面,用來接受後續傳入的邏輯,但是對輸入和輸出有要求

6.1 Supplier:代表一個輸出

 		Supplier<String> s1 = ()->{return "muxiaonong";};
        Supplier<String> s2 = ()->"muxiaonong2";
        System.out.println(s1.get());//輸出 muxiaonong
        System.out.println(s2.get());//輸出 muxiaonong2

6.2 Consumer:代表一個輸入

	    Consumer<String> c11 = (str) -> System.out.println(str);
        c11.accept("beijing");//輸出 beijing

6.3 BiConsumer:代表兩個輸入

		BiFunction<String,String,Integer> bf = (a,b)->a.length()+b.length();
        System.out.println(bf.apply("大吉大利", "今晚吃雞"));//輸出一個字串長度 8 

6.4 Function:代表一個輸入,一個輸出

//  Function<String,Integer>  用來接收後面的函式的實現,規定必須有一個輸入(String)有一個輸出(Integer)
 Function<String,Integer> f1 = (str)->{return str.length();};
 System.out.println(f1.apply("abcdefg"));//輸出長度 7 

七、方法的引用

  • 方法引用是用來直接訪問類或者例項的已經存在的方法或者構造方法,方法引用提供了一種引用而不執行方法的方式,如果抽象方法的實現恰好可以使用呼叫另外一個方法來實現,就有可能可以使用方法引用

7.1 方法引用的分類

型別 語法 對應的lambda表示式
靜態方法引用 類名::staticMethod (args) -> 類名.staticMethod(args)
例項方法引用 inst::instMethod (args) -> inst.instMethod(args)
物件方法引用 類名::instMethod (inst,args) -> 類名.instMethod(args)
構造方法引用 類名::new (args) -> new 類名(args)

7.2 靜態方法引用

  • 靜態方法引用: 如果函式式介面的實現恰好可以通過 呼叫一個靜態方法 來實現,那麼就可以使用靜態方法引用

/**
 * @program: lambda
 * @ClassName Test2
 * @description:
 * @author: muxiaonong
 * @create: 2020-10-28 22:15
 * @Version 1.0
 **/
public class Test2 {
	//無參靜態方法
	 static String put(){
	        System.out.println("put.....");
	        return "put";
	    }
	
	//有參靜態方法
	public static void getSize(int size){
	        System.out.println(size);
	    }
	
	 //有參 有返回值靜態方法
	 public static String toUpperCase(String str){
	        return str.toUpperCase();
	    }
	    
	//兩個入參,一個返回值靜態方法
    public static Integer getLength(String str,String str2){
        return str.length()+str2.length();
    }
	
	  public static void main(String[] args) {
	    //無參靜態方法-普通呼叫
		System.out.println(put());//輸出put
		//無參靜態方法-原生呼叫
		Supplier<String> s1 = ()-> Test2.put();
	    System.out.println(s1.get());//輸出put
		//無參靜態方法-靜態方法引用
		Supplier<String> s2 = Test2::put;
	    System.out.println(s2.get());//輸出put
		//無參靜態方法-內部類呼叫
	 	Supplier<String> s3 = Fun::hehe;
	    System.out.println(s3.get()); //輸出hehe
		
		// 有參靜態方法-靜態方法引用
		Consumer<Integer> c1 = Test2::getSize;
	    Consumer<Integer> c2 = (size)-> Test2.getSize(size);
	    c1.accept(123);
	    c2.accept(111);
	
		//有參有返回值靜態方法
		Function<String,String> f1 = (str)->str.toUpperCase();
	    Function<String,String> f2 = (str)-> Test2.toUpperCase(str);
	    Function<String,String> f3 = Test2::toUpperCase;
	    Function<String,String> f4 = Test2::toUpperCase;
	
	    System.out.println(f1.apply("abc"));//輸出 ABC 
	    System.out.println(f2.apply("abc"));//輸出 ABC 
	    System.out.println(f3.apply("abc"));//輸出 ABC 
	    System.out.println(f4.apply("abc"));//輸出 ABC 

		// 兩個引數 一個返回值 函式式介面
 		BiFunction<String,String,Integer> bf = (a, b)->a.length()+b.length();
        BiFunction<String,String,Integer> bf2 = Test2::getLength;
        System.out.println(bf2.apply("abc", "def"));//輸出 6
        System.out.println(bf.apply("abc", "def"));//輸出 6
	
	}

	//內部類
	class Fun {
	    public static String hehe(){
	        return "hehe";
	    }
	
	    public static String toUpperCase(String str){
	        return str.toUpperCase();
	    }
	
	}

}

7.3 例項方法引用

  • 例項方法引用: 如果函式式介面的實現恰好可以通過呼叫一個例項的例項方法來實現,那麼就可以使用例項方法引用
public class Test3 {
	//例項無參方法
    public String put(){
        return "put...";
    }

	//例項有參方法
    public void getSize(int size){
        System.out.println("size:"+size);
    }

	//例項有參有返回值方法
    public String toUpperCase(String str){
        return  str.toUpperCase();
    }
    public static void main(String[] args) {
    	//例項無參方法返回-普通呼叫
        System.out.println(new Test3().put());//輸出 put...
        Supplier<String> s1 = ()->new Test3().put();
        Supplier<String> s2 = ()->{return new Test3().put();};
        Supplier<String> s3 = new Test3()::put;
        System.out.println(s1.get());//輸出 put...
        System.out.println(s2.get());//輸出 put...
        System.out.println(s3.get());//輸出 put...

        //唯一的建立一個test3物件
        Test3 test = new Test3();

        Consumer<Integer> c1 = (size)->new Test3().getSize(size);
        Consumer<Integer> c2 = new Test3()::getSize;
        Consumer<Integer> c3 = test::getSize;


        c1.accept(123);//輸出 size:123
        c2.accept(123);//輸出 size:123
        c3.accept(123);//輸出 size:123

        Function<String,String> f1 = (str)->str.toUpperCase();
        Function<String,String> f2 = (str)->test.toUpperCase(str);
        Function<String,String> f3 = new Test3()::toUpperCase;
        Function<String,String> f4 = test::toUpperCase;
        System.out.println(f1.apply("abc"));//輸出 ABC
        System.out.println(f2.apply("abc"));//輸出 ABC
        System.out.println(f3.apply("abc"));//輸出 ABC
        System.out.println(f4.apply("abc"));//輸出 ABC
    }
}

7.4 物件方法引用

  • 物件方法引用: 抽象方法的第一個引數型別剛好是例項方法的型別,抽象方法剩餘的引數恰好可以當做例項方法的引數。如果函式式介面的實現能由上面說的例項方法呼叫來實現的話,那麼就可以使用物件方法引用
/** @Author mxn
 * @Description //TODO 物件方法引用
 * @Date 14:26 2020/11/7
 * @Param 
 * @return 
 **/
public class Test4 {
    public static void main(String[] args) {
        Consumer<Too> c1 = (too)->new Too().foo();
        c1.accept(new Too());//輸出 foo

        Consumer<Too> c2 = (Too too) ->new Too2().foo();
        c2.accept(new Too());//輸出 foo---too2

        Consumer<Too> c3 = Too::foo;
        c3.accept(new Too());//輸出 foo

        BiConsumer<Too2,String> bc = (too2,str)->new Too2().show(str);
        BiConsumer<Too2,String> bc2 = Too2::show;
        bc.accept(new Too2(),"abc");
        bc2.accept(new Too2(),"def");

        BiFunction<Exec,String,Integer> bf1 = (e,s)->new Exec().test(s);
        bf1.apply(new Exec(),"abc");
        BiFunction<Exec,String,Integer> bf2 = Exec::test;
        bf2.apply(new Exec(),"def");
    }
}

class Exec{
    public int test(String name){
        return 1;
    }

}

class Too{
    public Integer fun(String s){
        return 1;
    }
    public void foo(){
        System.out.println("foo");
    }
}
class Too2{
    public Integer fun(String s){
        return 1;
    }
    public void foo(){
        System.out.println("foo---too2");
    }

    public void show(String str){
        System.out.println("show ---too2"+str);
    }
}

7.5 構造方法引用

  • 構造方法引用: 如果函式式介面的實現恰好可以通過呼叫一個類的構造方法來實現,那麼就可以使用構造方法引用
/** @Author mxn
 * @Description //TODO 構造方法引用
 * @Date 14:27 2020/11/7
 * @Param 
 * @return 
 **/
public class Test5 {
    public static void main(String[] args) {
        Supplier<Person> s1 = ()->new Person();
        s1.get();//輸出 呼叫無參的構造方法
        Supplier<Person> s2 = Person::new;
        s2.get();//輸出 呼叫無參的構造方法

        Supplier<List> s3 = ArrayList::new;
        Supplier<Set> s4 = HashSet::new;
        Supplier<Thread> s5 = Thread::new;
        Supplier<String> s6 = String::new;

        Consumer<Integer> c1 = (age)->new Account(age);
        Consumer<Integer> c2 = Account::new;
        c1.accept(123);// 輸出 age 引數構造123
        c2.accept(456);//輸出 age 引數構造456

        Function<String,Account> f1 = (str)->new Account(str);
        Function<String,Account> f2 = Account::new;
        f1.apply("abc");//輸出 str 引數構造abc
        f2.apply("def");//輸出 str 引數構造def

    }
}

class Account{
    public Account(){
        System.out.println("呼叫無參構造方法");
    }

    public Account(int age){
        System.out.println("age 引數構造" +age);
    }

    public Account(String str){
        System.out.println("str 引數構造" +str);
    }
}

class Person{
    public Person(){
        System.out.println("呼叫無參的構造方法");
    }
}

八、小結

  • JAVA 8 引入 Lambda表示式是接收了函數語言程式設計語言的思想,和指令式程式設計相比,函數語言程式設計強調函式的計算比指令的執行重要。
  • lambda表示式可以使程式碼看起來簡潔,但一定程度上增加了程式碼的可讀性以及除錯的複雜性,所以在使用時應儘量是團隊都熟悉使用,要麼乾脆就別用,不然維護起來是件較痛苦的事,今天的小知識就到這裡了,有問題的小夥伴可以在下方進行留言,大家加油!

相關文章