天天看點

Java8的函數式程式設計

Java8釋出了一系列高效的操作方式,其中lambda就是一個很重要的特性。譬如我們可以利用lambda代替匿名内部類,可以更友善的建立線程,更友善的使用接口。同時Java8還結合lambda定義了一系列常用且高效的api,如forEach,Stream,Optional等等。

Lambda的起源

Lambda起源于20世紀40年代,是一種數學表達式λ,也是一種函數演算。這在和面向對象,面向過程并稱三大程式設計方式的函數式程式設計中用的極為廣泛。

其實不止Java,C++也早已引入了Lambda程式設計,同時學習Lambda我們要知道λ的始祖是Lisp語言,它的出現要早于C和Java。

我們可以稱lambda為函數式程式設計,什麼是函數式程式設計呢?這個要從λ演算說起。lambda演算要求一切皆函數,參數是函數,傳回值也是函數。有這個特點之後,就會給我們程式設計帶來很大的便捷性。假如說我們方法的參數可以是函數,那麼我們就可以把隻有一個方法的接口作為參數傳遞到函數中,再進一步來說,我們就可以通過lambda演算來代替匿名内部類,這也是lambda演算在Java中最重要的一個應用。

Lambda的使用

對于Java8來說,它提供了三個基礎的函數式接口,分别是

Consumer

Supplier

Function

,這三個接口場景不同,分别對應着消費,生産和函數,這三個接口在Java的java.util.function包中

1. Function

這個是最經典的函數式接口,它的作用就表現出了函數根本的作用:輸入入參,傳回出參,即:

R apply(T t)

。最常見的使用方式如下:

// 因為隻有一個入參,是以第一個括号中需要寫一個參數,且類型為T
// 因為隻有一個出參,是以函數體的doSomething需要傳回R類型
Function<T, R> func = (t) -> doSomething();
public R doSomething();
           

我們知道juf包中也有一個比較重要的函數式接口:

Predicate

,它的作用是輸入入參,傳回布爾值,即:

boolean test(T t)

。乍一看是一個新接口,其實它隻不過是

Function

的特殊形式,下面的兩個代碼塊作用是一樣的:

Predicate<String> predicate = (t) -> "test".equals(t);
System.out.println(predicate.test("111"));

Function<String, Boolean> func = (t) -> "test".equals(t);
System.out.println(func.apply("111"));
           

在JDK自帶的包中,也有很多使用到

Function

接口的方法,如我們在使用

Stream

時經常希望拿到list中對象的某一字段,就會使用

Stream#map(Function mapper)

,它通過map這個接口,将我們的參數進行了轉化。

除了

Predicate

之外,juf還對

Function

做了其他的延伸,如

BiFunction

ToDoubleFunction

ToDoubleBiFunction

等等,大家可以自行下去了解一下

2. Consumer

Consumer

對應着消費者,從字面意思來了解它的功能,應該是隻有入參,沒有出參的函數式接口:

void accept(T t)

。最常見的使用方式如下:

// 因為隻有一個入參,是以第一個括号中需要寫一個參數,且類型為T
// 因為沒有出參,是以函數體不能有傳回值
Consumer<String> consumer = (t) -> System.out.println(t);
consumer.accept(test);
           

Consumer

也在JDK自帶的很多包中使用,如我們常用的

Iterable#forEach(Consumer action)

,它的作用就是傳入集合中的元素,然後進行處理,不進行輸出。除此之外,

Optional#ifPresent(Consumer consumer)

也很好的解決了NPE的問題

Function

一樣,juf也對

Consumer

做了相當多的擴充,如

BiConsumer

,

DoubleConsumer

等等

3. Supplier

Supplier

Consumer

是一對,

Supplier

對應着生産者,它跟

Consumer

恰好相反,沒有函數入參,隻有函數出參:

R get()

。最常見的使用方式如下:

// 因為沒有入參,是以第一個括号中不應該有參數
// 因為隻有一個出參,是以傳回值的類型應該為R
Supplier<String> supplier = () -> “test”;
System.out.println(supplier.get());
           

相信大家在代碼開發的過程中都非常惡心NPE,是以JDK提供了

Optional

Optional

會通過

Supplier

來對NPE的異常進行重新抛出:

Optional#orElseThrow(Supplier exception)

,或者是通過

Optional#orElseGet(Supplier other)

去獲得另一個非空的參數,這幾個還是很不戳的

4. Custome

了解了上面的三種基礎函數式接口後,我們甚至也可以自定義一些我們需要的函數式接口,如

Comparator#compare(T o1, T o2)

,下面可以試着自定義一個

@FunctionalInterface
interface Test<A,B,C> {
  A test(B b, C c, int d);
}
Test<String, StringBuffer, Boolean> test = (b, c, d) -> b.append(d).append(d).append(c).toString();
System.out.println(test.test(new StringBuffer("111"), true, 1));
           

Lambda與設計模式

我們在日常開發工作中,常見的設計模式如政策模式,單例模式,工廠模式,模闆方法模式,建造者模式,代理模式,觀察者模式,責任鍊模式等等,我們如果把Lambda看成函數級别的接口的話,會大大簡化程式的代碼量的(其實也就是通過簡化類的建立來簡化代碼量)

1. 責任鍊模式

class ChainHandler {
    Function<String, String> first = (s) -> s + " fisrt process ";
    Function<String, String> second = (s) -> s + "second process ";
    Function<String, String> third = String::toUpperCase;

    public String runHandler(String input) {
      	return first.andThen(second).andThen(third).apply(input);
    }
}
           

2. 觀察者模式

class Observer {
    Consumer<String> alibaba = (msg) -> System.out.println("alibaba get the " + msg);
    Consumer<String> tencent = (msg) -> System.out.println("tencent get the " + msg);
    Consumer<String> baidu = (msg) -> System.out.println("baidu get the " + msg);

    // 注冊監聽者
    List<Consumer<String>> observers = ImmutableList.of(alibaba, tencent, baidu);

    public void notify(String msg) {
      	observers.forEach(e -> e.accept(msg));
    }
}
           

3. 政策模式

public String getSuitableEle(List<String> list, Predicate<String> strategy) {
    for (String s : list) {
        if (strategy.test(s)) {
          	return s;
        }
    }
    return null;
}
Predicate<String> hasJj = (s) -> s.contains("jj");
Predicate<String> notEmpty = (s) -> !s.isEmpty();
Predicate<String> end = (s) -> s.endsWith("666");
List<String> list = ImmutableList.of("12345","6666", "jj is big");
getSuitableEle(list, notEmpty);
getSuitableEle(list, notEmpty.and(hasJj));
getSuitableEle(list, notEmpty.and(hasJj).and(end));
           

後記

Java中的lambda我自己之前用的時候僅限于調用Stream,Optional,forEach等JDK原生的接口,從來沒有自己設計過對應的lambda,這次總結一下,也算是填補了之前的一個大空白