1. 程式人生 > >【java8新特性】蘭姆達表示式-2

【java8新特性】蘭姆達表示式-2

一、函式式介面

函式式介面(functional interface 也叫功能性介面,其實是同一個東西)。簡單來說,函式式介面是隻包含一個方法的介面。比如Java標準庫中的java.lang.Runnable和 java.util.Comparator都是典型的函式式介面。 
java 8提供 @FunctionalInterface作為註解,這個註解是非必須的,只要介面符合函式式介面的標準(即只包含一個方法的介面),虛擬機器會自動判斷, 但 最好在介面上使用註解@FunctionalInterface進行宣告,以免團隊的其他人員錯誤地往介面中新增新的方法。

Java中的lambda無法單獨出現,它需要一個函式式介面來盛放,lambda表示式方法體其實就是函式介面的實現.

下面的介面就是一個函式式介面


@FunctionalInterface //新增此註解後,介面中只能有一個抽象方法。
public interface A {
    void call();

}

二、lambda語法

包含三部分: 
1、一個括號內用逗號分隔的形式引數,引數是函式式接口裡面方法的引數 
2、一個箭頭符號:-> 
3、方法體,可以是表示式和程式碼塊。

(parameters) -> expression 或者 (parameters) -> { statements; } 

 

通過下面的程式碼可以看到lambda表示式設計的程式碼更==簡潔==,而且可讀性更好。



public class Demo1 {
    public static void main(String[] args) {
        runThreadByLambda();
        runThreadByInnerClass();
    }

    public static void runThreadByLambda() {
        /*
         Runnable就是一個函式式介面:他只有一個方法run()方法。
         1、因為run()方法沒有引數,所以   ->前面的()中不需要宣告形參
         2、run返回的是void,所以不需要return。
         3、->後面寫的程式碼其實就是定義在run方法內的程式碼。因為此處程式碼只有一行,所以{}也可以省略。如果此處多與一行,則無法省略。
         */
        Runnable runnable = () -> System.out.println("這個是用拉姆達實現的執行緒");
        new Thread(runnable).start();
    }

    public static void runThreadByInnerClass() {
        Runnable runnable = new Runnable() {

            @Override
            public void run() {
                System.out.println("這個是用內部類實現的執行緒");

            }
        };
        new Thread(runnable).start();
    }
}

三、方法引用

其實是lambda表示式的一種簡化寫法。所引用的方法其實是lambda表示式的方法體實現,語法也很簡單,左邊是容器(==可以是類名,例項名==),中間是”::”,右邊是相應的方法名。如下所示:

ObjectReference::methodName

一般方法的引用格式:

  1. 如果是靜態方法,則是ClassName::methodName。如 Object ::equals
  2. 如果是例項方法,則是Instance::methodName。如Object obj=new Object();obj::equals;
  3. 建構函式.則是ClassName::new

“`

public class Demo2 { 
public static void main(String[] args) { 
/* 
* 方法引用 
*/ 
Runnable runnable = Demo2::run; 
new Thread(runnable).start(); 
}

public static void run(){
    System.out.println("方法引用的程式碼...");
}

}

““

可以看出,doSomething方法就是lambda表示式的實現,這樣的好處就是,如果你覺得lambda的方法體會很長,影響程式碼可讀性,方法引用就是個解決辦法

五、使用lambda改進的集合框架



@FunctionalInterface
public interface A {
    void call();

    default void fun() {
        System.out.println("我是介面的預設方法1中的程式碼");
    }

    default void fun2() {
        System.out.println("我是介面的預設方法2中的程式碼");
    }

}

 

為什麼要有這個特性?首先,之前的介面是個雙刃劍,好處是面向抽象而不是面向具體程式設計,缺陷是,當需要修改介面時候,需要修改全部實現該介面的類,目前的 java 8之前的集合框架沒有foreach方法,通常能想到的解決辦法是在JDK裡給相關的介面新增新的方法及實現。然而,對於已經發布的版本,是沒法在給介面 新增新方法的同時不影響已有的實現。所以引進的預設方法。他們的目的是為了使介面沒有引入與現有的實現不相容發展。

java8中介面和抽象類的區別

形同點: 
++1.都是抽象型別; 
2.都可以有實現方法(以前介面不行); 
3.都可以不需要實現類或者繼承者去實現所有方法,(以前不行,現在介面中預設方法不需要實現者實現)++ 
不同點 
++1.抽象類不可以多重繼承,介面可以(無論是多重型別繼承還是多重行為繼承); 
2.抽象類和介面所反映出的設計理念不同。其實抽象類表示的是”is-a”關係,介面表示的是”like-a”關係; 
3.介面中定義的變數預設是public static final 型,且必須給其初值,所以實現類中不能重新定義,也不能改變其值;抽象類中的變數預設是 default 型,其值可以在子類中重新定義,也可以重新賦值。++

總結:預設方法給予我們修改介面而不破壞原來的實現類的結構提供了便利,目前java 8的集合框架已經大量使用了預設方法來改進了,當我們最終開始使用Java 8的lambdas表示式時,提供給我們一個平滑的過渡體驗。也許將來我們會在API設計中看到更多的預設方法的應用。

五、使用lambda改進的集合框架

5.2 Stream API

流(Stream)僅僅代表著資料流,並沒有資料結構,所以他遍歷完一次之後便再也無法遍歷(這點在程式設計時候需要注意,不像Collection,遍歷多少次裡面都還有資料),它的來源可以是Collection、array、io等等。

流作用是提供了一種操作大資料介面,讓資料操作更容易和更快。它具有過濾、對映以及減少遍歷數等方法,這些方法分兩種:中間方法和終端方法,“流”抽象天生就該是持續的,中間方法永遠返回的是Stream,因此如果我們要獲取最終結果的話,必須使用終點操作才能收集流產生的最終結果。區分這兩個方法是看他的返回值,如果是Stream則是中間方法,否則是終點方法。


import java.util.ArrayList;
import java.util.List;

public class Demo3 {
    public static void main(String[] args) {
        List<User> users = new ArrayList<User>();
        users.add(new User(20, "張三"));
        users.add(new User(22, "李四"));
        users.add(new User(10, "王五"));

        users.forEach((User user) -> System.out.println(user.getAge()));

    }

}

5.2 Stream API

流(Stream)僅僅代表著資料流,並沒有資料結構,所以他遍歷完一次之後便再也無法遍歷(這點在程式設計時候需要注意,不像Collection,遍歷多少次裡面都還有資料),它的來源可以是Collection、array、io等等。

流作用是提供了一種操作大資料介面,讓資料操作更容易和更快。它具有過濾、對映以及減少遍歷數等方法,這些方法分兩種:中間方法和終端方法,“流”抽象天生就該是持續的,中間方法永遠返回的是Stream,因此如果我們要獲取最終結果的話,必須使用終點操作才能收集流產生的最終結果。區分這兩個方法是看他的返回值,如果是Stream則是中間方法,否則是終點方法。

filter

在資料流中實現過濾功能是首先我們可以想到的最自然的操作了。Stream介面暴露了一個filter方法,它可以接受表示操作的Predicate實現來使用定義了過濾條件的lambda表示式。


import java.util.stream.Stream;

public class StreamDemo {
    public static void main(String[] args) {
        List<User> users = new ArrayList<User>();
        users.add(new User(20, "張三"));
        users.add(new User(22, "李四"));
        users.add(new User(10, "王五"));

        Stream<User> stream = users.stream();
        stream.filter(p -> p.getAge() > 20); //過濾年齡大於20的
    }

}

map

假使我們現在過濾了一些資料,比如轉換物件的時候。Map操作允許我們執行一個Function的實現(Function

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

public class StreamDemo {
    public static void main(String[] args) {
        List<User> users = new ArrayList<User>();
        users.add(new User(20, "張三"));
        users.add(new User(22, "李四"));
        users.add(new User(10, "王五"));

        Stream<User> stream = users.stream();
         //所有的年齡大於20歲的User物件,轉換為字串50物件。現在流中只有字串物件了。
        stream.filter((User user) ->  user.getAge() > 20).map((User user) -> {return "50";});

    }

}

count

count方法是一個流的終點方法,可使流的結果最終統計,返回long


import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collector;
import java.util.stream.Stream;

public class StreamDemo {
    public static void main(String[] args) {
        List<User> users = new ArrayList<User>();
        users.add(new User(20, "張三"));
        users.add(new User(22, "李四"));
        users.add(new User(10, "王五"));


        Stream<User> stream = users.stream();
        long count = stream.filter((User user) ->  user.getAge() >= 20).map((User user) -> {return "50";})
        .count(); //返回流中元素的個數。
        System.out.println(count);



    }

}