掃二維碼與項(xiàng)目經(jīng)理溝通
我們?cè)谖⑿派?4小時(shí)期待你的聲音
解答本文疑問(wèn)/技術(shù)咨詢/運(yùn)營(yíng)咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流
函數(shù)式編程functional programming(FP)要么是一種理念先進(jìn)的、應(yīng)該廣泛傳播的程序設(shè)計(jì)方法;要么是一種偏學(xué)術(shù)性的、實(shí)際用途不多的編程方式,下面為大家講解一下Java函數(shù)式編程使用方法。

Java8所有的新特性基本基于函數(shù)式編程的思想,函數(shù)式編程的帶來(lái),給Java注入了新鮮的活力。
下面來(lái)近距離觀察一下函數(shù)式編程的幾個(gè)特點(diǎn): ?函數(shù)可以作為變量、參數(shù)、返回值和數(shù)據(jù)類型。 ?基于表達(dá)式來(lái)替代方法的調(diào)用 ?函數(shù)無(wú)狀態(tài),可以并發(fā)和獨(dú)立使用 ?函數(shù)無(wú)副作用,不會(huì)修改外部的變量 ?函數(shù)結(jié)果確定性;同樣的輸入,必然會(huì)有同樣的結(jié)果。 下面jdk1.8里面對(duì)函數(shù)式編程的定義。只是一個(gè) FunctionalInterface 接口。特別的簡(jiǎn)單。
1 @Documented
2 @Retention(RetentionPolicy.RUNTIME)
3 @Target(ElementType.TYPE)
4 public @interface FunctionalInterface {}
這個(gè)函數(shù)式接口有幾點(diǎn)以下的限制: ?唯一的抽象方法,有且僅有一個(gè) (即所有的函數(shù)式接口,有且只能有一個(gè)抽象方法) ?加上標(biāo)注,則會(huì)觸發(fā)JavaCompiler的檢查。對(duì)于符合函數(shù)接口的接口,加不加都無(wú)關(guān)緊要,但是加上則會(huì)提供一層編譯檢查的保障。如果不符合,則會(huì)報(bào)錯(cuò)。 ?不能被覆蓋之后,再聲明為抽象方法,則不算抽象方法。例如接口實(shí)現(xiàn)了Object中的方法。 ?可用于lambda類型的使用方式
Stream的操作是建立在函數(shù)式接口的組合之上的。Java8中新增的函數(shù)式接口都在java.util.function包下。這些函數(shù)式接口可以有多種分類方式。
2.1 Function
Function是從T到R的一元映射函數(shù)。將參數(shù)T傳遞給一個(gè)函數(shù),返回R。即R = Function(T)
Function最常用的應(yīng)該是 Stream map(Function mapper);
比如List person里面有age,name…. 我傳入age,他就會(huì)返回age的集合給我。
@FunctionalInterface
public interface Function {
R apply(T t);
default Function compose(Function before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
default Function andThen(Function after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
static Function identity() {
return t -> t;
}
}
2.2 Predicate
Predicate是一個(gè)謂詞函數(shù),主要作為一個(gè)謂詞演算推導(dǎo)真假值存在,返回布爾值的函數(shù)。Predicate等價(jià)于一個(gè)Function的boolean型返回值的子集。
predicate最常用的莫過(guò)于 Stream filter(Predicate predicate);
比如我要過(guò)濾年齡 > 18 的人,我傳入age,判斷是否為true。為true則保留,false丟棄。
@FunctionalInterface
public interface Predicate
{
boolean test(T t);
default Predicate and(Predicate other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
default Predicate negate() {
return (t) -> !test(t);
}
default Predicate or(Predicate other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
static Predicate isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}
2.3 Consumer
Consumer是從T到void的一元函數(shù),接受一個(gè)入?yún)⒌环祷厝魏谓Y(jié)果的操作。
Consumer最常用的肯定是 default void forEach(Consumer action) {}
這是一段forEach循環(huán)的代碼,傳入實(shí)現(xiàn)的方法,并不返回任何值。只是循環(huán)。
@FunctionalInterface
public interface Consumer {
void accept(T t);
default Consumer
andThen(Consumer after) { Objects.requireNonNull(after); return (T t) -> { accept(t); after.accept(t); }; } }
3.1 基本語(yǔ)法
Lambda 的基本結(jié)構(gòu)為 (arguments) -> body,有如下幾種情況: ?參數(shù)類型可推導(dǎo)時(shí),不需要指定類型,如 (a) -> System.out.println(a) ?當(dāng)只有一個(gè)參數(shù)且類型可推導(dǎo)時(shí),不強(qiáng)制寫(xiě) (), 如 a -> System.out.println(a) ?參數(shù)指定類型時(shí),必須有括號(hào),如 (int a) -> System.out.println(a) ?參數(shù)可以為空,如 () -> System.out.println(“hello”) ?body 需要用 {} 包含語(yǔ)句,當(dāng)只有一條語(yǔ)句時(shí) {} 可省略
3.2 Lambda原理
比如如下代碼:
List list = new ArrayList();
list.stream().filter((x) -> x >= 18)
Stream filter(Predicate predicate);
@FunctionalInterface
public interface Predicate {
boolean test(T t);
}
比如List里面存?zhèn)€個(gè)人的年齡,現(xiàn)在篩選出年齡大于等于18的人。
此時(shí)我們就可以用 list.stream().filter((x) -> x >= 18) 這就是一個(gè)典型的lambda表達(dá)式
(x) -> x >= 18 傳給 Predicate 函數(shù)式接口。
原理其實(shí)是:
JVM幫我們動(dòng)態(tài)生成了一個(gè)內(nèi)部類,然后這個(gè)內(nèi)部類實(shí)現(xiàn)了 Predicate 這個(gè)函數(shù)式接口。
重寫(xiě)了里面的test方法。生成的類似如下:
static final class Main$$Lambda$1 implements Predicate {
private Main$$Lambda$1() {
}
@Override
public boolean test(Integer x) {
return x >= 18;
}
}
3.3 Lambda用法
public class Main {
public static void main(String[] args) {
List list = new ArrayList();
list.add(40);
list.add(50);
list.add(20);
list.add(30);
List collect = list.stream().filter(x -> x >= 30)
.map((x) -> x + 10).sorted((x, y) -> -x.compareTo(y))
.collect(Collectors.toList());
System.out.println(collect);
}
}
這個(gè)一段很典型的Lambda + Stream的用法。 ?list.stream()獲取list的stream的流 ?filter篩選出年齡大于30的人 (里面是一個(gè)Predicate接口,返回真假) ?map做一個(gè)function映射 ?sort排序,里面是compartor
Lambda 表達(dá)式可以減少很多代碼,能提高生產(chǎn)力。但也要理解其原理。比如3.3中的代碼,為什么filter里面是斷言表達(dá)式,map里面是function表達(dá)式。
這都要從lambda的原理入手,也就是JVM動(dòng)態(tài)生成一個(gè)內(nèi)部類,并繼承其中的抽象方法。
本次主要介紹了Java函數(shù)式編程的原理以及應(yīng)用,主要從Stream和lambda入手。通過(guò)一些簡(jiǎn)單的概念,以及代碼,更好的理解Java的函數(shù)式編程。
掌握J(rèn)ava的函數(shù)式編程,對(duì)平時(shí)我們開(kāi)發(fā)代碼,看其他人的代碼,都有很大的幫助。

我們?cè)谖⑿派?4小時(shí)期待你的聲音
解答本文疑問(wèn)/技術(shù)咨詢/運(yùn)營(yíng)咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流