java8 - 內置函數式接口(總結)及方法引用

幾個使用場景

上面幾個,是否像咱們調用一個方法,調用方法的時候,有這幾種形式:java

  • 傳參數,有返回值:Predicate,Function
  • 傳參數,無返回值:Consumer
  • 不傳參數,有返回值:Supplier

因此咱們調用匿名類的時候,基本均可以用上面幾種內置的函數式接口,而不用本身特地去爲了一個匿名類定義接口。segmentfault

其餘函數式接口

java.util.function包中,咱們能夠看到IntPredicateIntFunctionIntSupplierIntConsumerLongConsumerLongFunctionLongPredicateLongSupplier等,是爲了在輸入和輸出都是原始類型時,避免自動裝箱的操做。
java的自動裝箱機制雖然可讓咱們在原始類型和引用類型之間的裝箱和拆箱操做是自動完成的,但這在性能方面是要付出代價的。裝箱後的值本質上就是把原始類型包裹起來,並保存在堆裏。所以,裝箱後的值須要更多的內存,並須要額外的內存搜索來獲取被包裹的原始值。
好比下面這個,是沒有裝箱的:app

IntPredicate evenNumbers = (int i) -> i % 2 == 0;
evenNumbers.test(1000);

下面這個,是有裝箱的:ide

Predicate<Integer> oddNumbers = (Integer i) -> i % 2 == 1;
oddNumbers.test(1000);

方法引用

在java8中,方法引用是調用特定方法的Lambda的一種快捷寫法,格式是類名+::+方法,注意後面沒有括號,表示沒有實際調用這個方法。咱們下面看看例子:函數

靜態方法引用

把一組字符串轉爲數字,並求和

先解析一下,把字符串轉數字,就是輸入字符串,返回數字,因此咱們用的是Function這個內置函數式接口,因此咱們是這樣寫的:性能

public class StaticMethod {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("1", "2", "3", "4");
        // 方法1
        sum(list, (String str) -> Integer.valueOf(str));
        // 方法2
        sum(list, Integer::parseInt);
    }

    public static <T, R> void sum(List<T> list, Function<T, R> function) {
        Integer sum = 0;
        for (T t : list) {
            sum += (Integer) function.apply(t);
        }
        System.out.println(sum);
    }
}

方法1代碼中,咱們能夠看到Integer.valueOf()是靜態方法,接收的參數就是傳遞過來的參數,因此能夠直接用方法2來簡寫,說明引用的是Integer的valueOf方法。this

任意類型實例方法的方法引用

在前面的Function中,咱們演示了經過字符串,返回對應的長度這個例子,裏面有個Lambda表達式
t -> t.length(),這個t是String類型的,調用的是length()方法,因此咱們能夠直接這樣寫:code

public class StaticMethod2 {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("1", "2", "3", "11", "22", "33");
        getLength(list, String::length);
    }

    public static <T, R> void getLength(List<T> list, Function<T, R> function) {
        Map<T, R> map = new HashMap<>();
        for (T t : list) {
            map.put(t, function.apply(t));
        }
        System.out.println(map);
    }
}

String::length來替換t -> t.length(),說明引用的是String的length方法。這個與靜態方法引用不一樣的是,靜態方法引用中,調用的是其餘對象的靜態方法,而這裏是調用的是Lambda參數的方法(好比這個例子的參數類型是string,調用的的length方法)。對象

現有對象的實例方法的方法引用

Supplier中,咱們看了獲取字符串的例子,咱們把這個稍微改一下,獲取字符串的長度。接口

public class StaticMethod3 {
    public static void main(String[] args) {
        String str = "hello wolrd";
        // 第一個方法
        System.out.println(getStrLength(() -> str.length()));
        // 第二個方法
        System.out.println(getStrLength(str::length));
    }
    public static <T> T getStrLength(Supplier<T> supplier) {
        return supplier.get();
    }
}

第一個方法,是以前的寫法,咱們能夠看到調用的是str的length
方法,因而咱們能夠直接簡寫str::length,一樣是調用字符串的length方法,上面例子是String::length,這邊是str::length,區別在於這邊是調用已經存在的外部對象(str)中的方法。

構造函數引用

先定義兩個class,一個有參構造,一個無參構造:

class ArgBean {
    Integer arg;

    public ArgBean(Integer arg) {
        this.arg = arg;
    }

    @Override
    public String toString() {
        return "ArgBean{" +
                "arg=" + arg +
                '}';
    }
}

無參構造

無參構造,很明顯咱們用Supplier,在第一種方法到第二種方法的簡化,直接是類+::+new。

public class Constructor {
    public static void main(String[] args) {
        // 第一種方法
        Supplier<Bean> supplier1 = () -> new Bean();
        System.out.println(supplier1.get());
        // 第二種方法
        Supplier<Bean> supplier2 = Bean::new;
        System.out.println(supplier2.get());

    }
}

有參構造

有參構造,經過一個對象返回另一個對象,因此咱們用Function。方式跟上面同樣,直接是類+::+new,不一樣的是須要經過apply傳遞參數

public class Constructor {
    public static void main(String[] args) {
        // 第一種方法
        Function<Integer, ArgBean> function = (arg) -> new ArgBean(arg);
        System.out.println(function.apply(1));
        // 第二種方法
        Function<Integer, ArgBean> function2 = ArgBean::new;
        System.out.println(function2.apply(2));
    }
}
相關文章
相關標籤/搜索