java8之lambda表達式

java8

近期,在一個不完全的統計中,java8的普及率已經到達了近80%。
圖【1】
相比之前的java版本,下面兩個是java8出現帶來最大的影響:其一是極大地簡化了代碼的複雜度尤其是在處理集合以及接口這兩個方面。除此之外,java8引入了流概念,來避免或減少使用多線程的同時利用機器多核來加快代碼的執行效率。

本篇文章主要介紹java8的第一個特性————lambda表達式。

介紹:

在java7及更早的版本,當我們會遇到這樣的場景:當要實現一個接口尤其是匿名類接口,往往需要寫很多模板式的代碼;當我們要遍歷一個集合做出篩選的時候會使用很多for循環以及if判斷語句,這些代碼往往導致代碼顯得十分臃腫。
而java8出現的Lambda表達式可以很好地解決上面的問題。它可以讓你很簡潔地表示一個行爲或傳遞代碼。你可以把Lambda表達式看作匿名的功能,它基上就是沒有聲明名稱的方法,但和匿名類一樣,它也可以作爲參數傳遞給一個方法。
例子:

        //普通表達式1
        Runnable runnable2 = new Runnable() {
            @Override
            public void run() {
                System.out.println("a");
            }
        };
        Thread thread1 = new Thread(runnable2);
        thread1.start();

        //lambda表達式1
        Runnable runnable = () -> System.out.println("a");
        Thread thread=new Thread(runnable);
        thread.start();

        //普通表達式2
        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("a");
            }
        });

        //lambda表達式2
        Thread thread3 = new Thread(()-> System.out.println("a"));
        thread.start();

上面的例子通過runnable例子分別使用了普通的匿名類和lambda的方式實現線程接口。可以發現使用Lambda表達式可以使我們的代碼變得更加簡潔。

在哪裏使用:

當然,並不是所有的接口都能使用lambda表達式。只有是函數型接口的時候才能使用。
函數型接口指接口只定義了一個抽象方法的接口。例如ranable方法,只定義了一個run方法:

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

其中,爲了更好的支持函數式接口,在java8中增加了@FunctionalInterface註解,只要使用了@FunctionalInterface註解的接口只能定義一個抽象方法,否則就會報錯。

如何使用:

案例

我們可以通過一個例子一步步掌握和熟悉lambda表達式。以下例子來自java 8實戰
首先有一個蘋果類:

public class Apple {
    private String color;
    private int weight;

    public Apple() {
    }

    public Apple(String color, int weight) {
        this.color = color;
        this.weight = weight;
    }
    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public int getWeight() {
        return weight;
    }

    public void setWeight(int weight) {
        this.weight = weight;
    }

    @Override
    public String toString() {
        return "Apple{" +
                "color='" + color + '\'' +
                ", weight=" + weight +
                '}';
    }
}

第一次嘗試

現在我們要從蘋果堆中找出綠色的蘋果。

public static List<Apple> filterGreenApples(List<Apple> inventory) {
        List<Apple> result = new ArrayList<Apple>();
        for (Apple apple : inventory) {
            if ("green".equals(apple.getColor())) {
                result.add(apple);
            }
        }
        return result;
    }

我們傳入一堆蘋果,逐個判斷他的顏色是否是綠色,但是這種方式不利於代碼的擴展,當我們需要篩選紅色、白色、黃色蘋果都需要修改代碼。

第二次嘗試:顏色參數

我們可以給方法傳一個顏色的參數,來滿足篩選不同顏色的蘋果:

public static List<Apple> filterApplesByColor(List<Apple> lists, String color) {
        List<Apple> result = new ArrayList<>();
        for (Apple apple : lists) {
            if (apple.getColor().equals(color)) {
                result.add(apple);
            }
        }
        return result;
    }

但是,有一天我們需要篩選不同重量的蘋果時,上述的方法不能滿足我們需求了,於是我們又新建了另一個方法:

 public static List<Apple> filterApplesByWeight(List<Apple> inventory, int weight) {
        List<Apple> result = new ArrayList<Apple>();
        for (Apple apple : inventory) {
            if (apple.getWeight() > weight) {
                result.add(apple);
            }
        }
        return result;
    }

雖然滿足我們的需求,但是篩選蘋果和篩選重量的方法大部分的代碼是一樣的,只有判別的方式不同,我們可以提取大部分的代碼。

第三次嘗試:對你能想到的每個屬性做篩選

第三種嘗試,是增加了字段來判斷是來判斷蘋果還是重量。

public static List<Apple> filterApples(List<Apple> inventory, String color, int weight, boolean flag) {
        List<Apple> result = new ArrayList<Apple>();
        for (Apple apple : inventory) {
            if ((flag && apple.getColor().equals(color)) || (!flag && apple.getWeight() > weight)) {
                result.add(apple);
            }
        }
        return result;
    }

但是仔細思考之後,這隻方式反而是最不好的。首先,他還是隻能篩選一個條件,其次,他還是不能做擴展,例如篩選大小,形狀等等。

第四次嘗試:行爲參數化

我們可以創建一個接口,用它來傳遞我們的篩選規則

public interface ApplePredicate{
    boolean test (Apple apple);
}
public static <T> List<T> filterApple(List<T> lists, ApplePredicate<T> predicate) {
        List<T> result = new ArrayList<>();
        for (T e : lists) {
            if (predicate.test(e)) {
                result.add(e);
            }
        }
        return result;
    }
public class AppleRedAndHeavyPredicate implements ApplePredicate{ public boolean test(Apple apple){
            return "red".equals(apple.getColor())
                && apple.getWeight() > 150;
} }
List<Apple> redAndHeavyApples =filterApples(inventory, new AppleRedAndHeavyPredicate());

這樣可以通過用接口的方式來滿足我們不同對的蘋果進行篩選。

第五次嘗試:匿名內部類

但是在第四次嘗試中,我們每次新的篩選而創建的類只是用一次,可以使用匿名類類的方式來實現接口。

List<Apple> redApples = filterApples(list, new ApplePredicate() {
            @Override
            public Boolean test(Apple apple) {
                return "red".equals(apple.getColor());
            }
        });

第六次嘗試:lambda表達式

上面的代碼在java 8裏可以用Lambda表達式重寫爲下面的樣子:

List<Apple> result =
filterApples(inventory, (Apple apple) -> "red".equals(apple.getColor()));

java原生接口

在java8中增加了原生的函數式接口提供我們來使用lambda,例如所有java.util.function 包下的接口都是函數式接口,都可以使用lambda表達式。

@FunctionalInterface
public interface Supplier<T> {
    T get();
}
@FunctionalInterface
    public interface Function<T, R>{
        R apply(T t);
    }
@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);
}

速度測試

實質分析

存在的問題

發佈了97 篇原創文章 · 獲贊 19 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章