java8新特性function和lambda深度解析

繼續java8新亮點的源碼之路,functional interface是一個跳不過的坎,它與lambda的結合使用非常普遍。**java.util.function**包對於每一個java工程師來說是必備技能,也是最基礎的能力,一定要掌握。

java8新特性function和lambda深度解析

函數編程的最直接的表現在於將函數作為數據自由傳遞,結合泛型推導能力使代碼表達能力獲得飛一般的提升。同時Lambda表達式讓你能夠將函數作為方法參數或者將代碼作為數據對待,讓你發現“行級代碼”優美。

java8引入新的註解,@FunctionalInterface

函數式註解@FunctionalInterface添加在一個接口上,主要是編譯器檢查提示作用。

1. 註解的作用是檢測自定義functional接口是否符合要求,編譯器會有錯誤提示;

2. 一個接口符合functional的要求,不加這個註解也可以正常使用,**建議都加上**;

3. 有且只能有一個抽象方法但可以有多個非抽象方法,簡單說就是接口裡面default和static的方法是可以有多個的,其他的方法只能有一個。

lambda表達式寫法及注意點

> 格式:`( parameters ) -> { statements; }`。

1. 不需要聲明參數類型,編譯器可以識別參數值;

2. 單個參數和語句下,圓括弧和大括弧可以省略;

3. 表達式是一個閉包,定義了行內執行的方法類型接口;

4. 只能引用標記了final的外層局部變量,不能在表達式內部修改定義在域外的局部變量,否則會編譯錯誤;

5. 表達式當中不允許聲明一個與局部變量同名的參數或者局部變量;

寫法示例:

@FunctionalInterface
public interface IPerson {
String say(String input);
//void stand(); 只能有一個抽象方法,不然編譯無法默認識別調用
static void run(String xx){
PrintUtil.printTest("IPerson run : " + xx);
}
static void walk(){
PrintUtil.printTest("IPerson walk");
}
default void eat(int a, int b){
PrintUtil.printTest("IPerson eat : " + a + " - " + b);
}
}
//當你這種寫法是編譯器會提示你用lambda
IPerson person = new IPerson() {
@Override
public String say(String input) {
return "My said is " + input;
}
};
PrintUtil.printTest(person.say("i love china."));
//lambda寫法
IPerson person2 = a -> "My said is " +a;
PrintUtil.printTest(person2.say("i love china."));
//結果是一樣的,My said is i love china.

function包中重要接口源碼分析

java8新特性function和lambda深度解析

Consumer,接收一個輸入參數T類型並不沒有返回值;andThen看源碼可以知道是添加一個其後執行的Consumer對象。

這個接口很簡單不需要什麼解釋,看源碼一眼OK。

@FunctionalInterface
public interface Consumer {
void accept(T t);
default Consumer andThen(Consumer super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}

Function,接收一個T類型參數,返回一個R類型的結果。需要注意的是compose\\andThen的傳入參數和範圍參數規則不同,這裡的參數類型稍有不慎就會出錯,複雜的鏈路裡面排查bug是非常麻煩的事。

@FunctionalInterface

public interface Function {

R apply(T t);

//生成了function的參數類型同before一樣

default Function compose(Function super V, ? extends T> before) {

Objects.requireNonNull(before);

return (V v) -> apply(before.apply(v));

}

//新生成的function的返回值類型要after一樣

default Function andThen(Function super R, ? extends V> after) {

Objects.requireNonNull(after);

return (T t) -> after.apply(apply(t));

}

//

static Function identity() {

return t -> t;

}

}

測試用例:

Function<integer> function = a -> "== " + a;
PrintUtil.printTest(function.apply(101));
Function<string> function1 = c -> c.length()>2;
PrintUtil.printTest(function1.apply("1a"));
PrintUtil.printTest(function.andThen(function1).apply(111));
//andThen類似consumer,是前一個function執行後結果作為參數傳新生成的function執行,結構:true

Function<integer> function2 = c -> c*c;
//compose和andThen正好邏輯相反,傳入的參數function先執行後範圍結果作為參數傳給新生成的function執行
PrintUtil.printTest(function.compose(function2).apply(2));
//先執行function2,返回結果作為參數再執行function,結果:== 4
PrintUtil.printTest(function.compose(function2).andThen(function1).apply(2));
//先執行function2,其次執行funciton,最後執行function1,結果:true
PrintUtil.printTest(function2.compose(function2).apply(2));
//先執行第二個function2,返回結果作為參數再執行第一個function2,結果:16
//遞歸的實現又多了種辦法
Function<string> function3 = Function.identity();//static方法
PrintUtil.printTest(function3.apply("hello"));
//identity定義了一個只返回輸入參數的function,結果:hello
/<string>/<integer>/<string>/<integer>
java8新特性function和lambda深度解析

Predicate,是一個條件判斷接口,接收一個T參數範圍boolean值,默認抽象方法test(t);and\\or\\negate分別對應邏輯與、或、非操作,isEqual。

@FunctionalInterface

public interface Predicate {

boolean test(T t);

//邏輯與

default Predicate and(Predicate super T> other) {

Objects.requireNonNull(other);

return (t) -> test(t) && other.test(t);

}

//獲取該對象否定的Predicate,相當於逆轉boolean

default Predicate negate() {

return (t) -> !test(t);

}

//邏輯或

default Predicate or(Predicate super T> other) {

Objects.requireNonNull(other);

return (t) -> test(t) || other.test(t);

}

//生成一個判斷對象是否相等的Predicate

static Predicate isEqual(Object targetRef) {

return (null == targetRef)

? Objects::isNull

: object -> targetRef.equals(object);

}

}

測試用例:

Predicate<icar> carHas4Wheel = a -> a.getWheelCount() > 3;/<icar>

ICar weilai = new WeiLaiCar(3);

PrintUtil.printTest(carHas4Wheel.test(weilai));

//false

PrintUtil.printTest(carHas4Wheel.negate().test(weilai));

//true

PrintUtil.printTest(carHas4Wheel.and(b -> b.getWheelCount()>2).test(weilai));

//false

PrintUtil.printTest(carHas4Wheel.and(b -> b.getWheelCount()>2).test(new WeiLaiCar(4)));

//true

PrintUtil.printTest(carHas4Wheel.or(b -> b.getWheelCount()>2).test(new WeiLaiCar(3)));

//true

PrintUtil.printTest(Predicate.isEqual(weilai).test(weilai));

//true

PrintUtil.printTest(Predicate.isEqual(weilai).test(new WeiLaiCar(3)));

//false

Supplier,是一個只有返回沒有參數的接口,只有一個方法get。

@FunctionalInterface
public interface Supplier {
/**
* Gets a result.
* @return a result
*/
T get();
}

測試用例:

Supplier<integer> supplier = () -> 1 ;
PrintUtil.printTest(supplier.get());
//1
Supplier<icar> carSupplier = () -> new WeiLaiCar(5) ;
PrintUtil.printTest(carSupplier.get() + " : " + carSupplier.get().getWheelCount());
//com.ts.util.optional.WeiLaiCar@32a1bec0 : 5
/<icar>/<integer>
java8新特性function和lambda深度解析

Consumer Predicate Supplier Function BiFunction

這裡的T U R V並沒有特別的定義只是一種約定,就像駝峰命名和泛型中K V E一樣。

分享連接

除了介紹的幾種函數式接口外,java8在這幾個基礎上封裝了很多的延伸接口,如BiConsumer\\ DoubleConsumer\\ IntConsumer\\ LongConsumer\\ ObjIntConsumer等。平時寫代碼時注意積累,閒的時候多去看看API,慢慢的就掌握了。

Lambda和Functional的結合很大一點就是代碼簡潔了,看著非常的賞心悅目。JAVA一個純面向對象的語言,在行級代碼上一直是非常的簡潔易於調試,而這兩個新特性的出現讓行級代碼的複雜度急劇提升。

相關用例代碼已託管Github:https://github.com/owen-jia/play-java-sample.git 。

推薦關注

推薦關注他的博客:https://blog.shareworld.vip


分享到:


相關文章: