JDK8新特性Lambda表達式體驗

「Lambda 表達式」(lambda expression)是一個匿名函數,Lambda表達式基於數學中的λ演算得名,直接對應於其中的lambda抽象(lambda abstraction),是一個匿名函數,即沒有函數名的函數。Java 8的一個大亮點是引入Lambda表達式,使用它設計的代碼會更加簡潔。當開發者在編寫Lambda表達式時,也會隨之被編譯成一個函數式接口。html

Lambda簡介

Lambda表達式的語法由參數列表、箭頭符號->和函數體組成。函數體既能夠是一個表達式,也能夠是一個語句塊。java

好比:express

(int x, int y) -> x + y;

具體的Lambda表達式的介紹能夠看這篇博客,寫得挺詳細的。編程

Java Lambda表達式示例

下面就用一些例子來體驗一下Lambda表達式。編程語言

遍歷集合

好比咱們如今要遍歷一個List:ide

List<String> list = Arrays.asList("Hello", "JDK8", "and", "Lambda");

JDK8以前的寫法:函數式編程

for (String s : list) {
    System.out.println(s);
}

用Lambda表達式寫法:函數

list.forEach(s -> System.out.println(s));

能夠看到,不管是代碼量和可讀性都獲得了提升。線程

在此基礎上還能夠再用隱式表達式進行簡化:設計

list.forEach(System.out::println);

匿名類

在Java中不少時候咱們要用到匿名類,好比線程Runnable、FileFilter和Comparator等等。
而匿名類型最大的問題就在於其冗餘的語法。

這裏用Comparator作例子。

好比咱們有一個Cat類,表示貓,有名字、高度和重量這些屬性。

package com.fengyuan.model;

import lombok.AllArgsConstructor;
import lombok.Data;

public @Data @AllArgsConstructor class Cat {
    private String name;
    private double height;
    private double weight;
}

咱們建立3只貓,存到List中:

List<Cat> catList = new ArrayList<>();
// 請無視這些數據的合理性,我亂寫的
catList.add(new Cat("cat1", 10.3, 3.6));
catList.add(new Cat("cat2", 9.3, 4.6));
catList.add(new Cat("cat3", 9.5, 4.0));

而後咱們如今要對這個List進行排序,可是如今不知道是要怎麼排,因此咱們要定義一個比較器,指定用高度或者是重量來排序。

JDK8以前的寫法:

// 指定用高度來排序
Collections.sort(catList, new Comparator<Cat>() {
    @Override
    public int compare(Cat o1, Cat o2) {
        if (o1.getHeight() > o2.getHeight()) {
            return 1;
        } else if (o1.getHeight() < o2.getHeight()) {
            return -1;
        } else {
            return 0;
        }
     }
});

而用Lambda,能夠這樣寫:

Collections.sort(catList, (o1, o2) -> {
    if (o1.getHeight() > o2.getHeight()) {
        return 1;
    } else if (o1.getHeight() < o2.getHeight()) {
        return -1;
    } else {
        return 0;
    }
});

繼續用方法引用,能夠簡寫到極致:

// 指定用重量排序
catList.sort(Comparator.comparing(Cat::getWeight));
// 要逆向排列也很簡單
catList.sort(Comparator.comparing(Cat::getWeight).reversed());

到最後這種寫法,已經簡寫到極致,並且可讀性很是高。

函數式接口

JDK8增長了一個新的包:java.util.function,它裏面包含了經常使用的函數式接口,好比Predicate<T>、Consumer<T>,Function<T, R>等等。

接下來就體驗一下Predicate和Consumer的用法。

咱們如今有一個訂單類,有id,金額,運費這些屬性。這個訂單有一個折扣方法,咱們但願可以根據營銷活動,動態修改優惠方案。
Order類:

package com.fengyuan.model;

import java.util.function.Consumer;
import java.util.function.Predicate;

import lombok.AllArgsConstructor;
import lombok.Data;

public @Data @AllArgsConstructor class Order {
    private long id;
    private double payment;
    private double freight;

    // 優惠政策
    public Order discount(Order order, Predicate<Order> predicate, Consumer<Order> consumer) {
        // 知足Predicate的條件,返回true
        if (predicate.test(order)) {
            // 接收訂單對象,對訂單對象進行處理
            consumer.accept(order);
        }

        return order;
    }
}

其中

  • Predicate<T>:接收T對象並返回boolean。

  • Consumer<T>:接收T對象,沒有返回值。

而後經過函數式編程,咱們能夠動態傳入咱們的優惠方案,好比99包郵:

// 新建一個訂單,506.5的金額,10.0的運費
Order order = new Order(123, 506.5, 10.0);
// 知足金額>=99的條件,則設置運費爲0
order.discount(order, 
        o -> o.getPayment() >= 99, 
        o -> o.setFreight(0));

這樣一來,就能根據營銷活動,修改咱們的優惠方案。

除此以外,Predicate對象之間還能運用與或非這些邏輯操做,好比:

predicate1.and(predicate2);
predicate1.or(predicate2);

Stream

這裏的Stream和I/O流不一樣,它更像具備Iterable的集合類。

Stream API引入的目的在於彌補Java函數式編程的缺陷,讓java也支持map()、reduce()等函數式編程語言。

map

map(映射),將傳入的函數依次做用到序列的每一個元素。

好比說,有一個字符串列表,咱們如今給列表裏每一個字符串調用toLowerCase()方法,轉成小寫字母。

List<String> list = Arrays.asList("Hello", "JDK8", "and", "Lambda");

轉成小寫,用collect()把Stream再轉回List,返回新的列表:

List<String> newList = list.stream().map(s -> s.toLowerCase()).collect(Collectors.toList());

也能夠返回一個字符串,指定鏈接符,我這裏是用空格鏈接的:

String str = list.stream().map(s -> s.toLowerCase()).collect(Collectors.joining(" "));

也能夠用隱式函數,String::toLowerCase來實現:

String str = list.stream().map(String::toLowerCase).collect(Collectors.joining(" "));

reduce

reduce(歸約),將集合中全部值結合起來。

將一個整型List,先進行map:每一個數都翻一倍,再進行reduce:全部數加起來,獲得結果:

List<Integer> numbers = Arrays.asList(10, 20, 30, 40, 50);
        int result = numbers.stream().map(num -> num * 2).reduce((r, num) -> r += num).get();

變量捕捉

一個簡單的例子,算出一個集合中最大值、最小值、平均值等等。

集合:

List<Integer> numbers = Arrays.asList(4, 6, 65, 3, 44, 2, 17, 19);

計算:

int max = numbers.stream().mapToInt(x -> x).max().getAsInt();
int min = numbers.stream().mapToInt(x -> x).min().getAsInt();
long count = numbers.stream().mapToInt(x -> x).count();
double avg = numbers.stream().mapToInt(x -> x).average().getAsDouble();
int sum = numbers.stream().mapToInt(x -> x).sum();

也能夠用IntSummaryStatistics類來獲得統計結果:

IntSummaryStatistics stat = numbers.stream().mapToInt(x -> x).summaryStatistics();
int max = stat.getMax();
int min = stat.getMin();
long count = stat.getCount();    
double avg = stat.getAverage();
long sum = stat.getSum();

結語

雖然平時項目的開發中仍是比較少用到Lambda表達式,可是在以上這些體驗中,確實是感覺到了它的魅力。

相關文章
相關標籤/搜索