繼續java8新亮點的源碼之路,functional interface是一個跳不過的坎,它與lambda的結合使用非常普遍。**java.util.function**包對於每一個java工程師來說是必備技能,也是最基礎的能力,一定要掌握。
函數編程的最直接的表現在於將函數作為數據自由傳遞,結合泛型推導能力使代碼表達能力獲得飛一般的提升。同時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包中重要接口源碼分析
Consumer,接收一個輸入參數T類型並不沒有返回值;andThen看源碼可以知道是添加一個其後執行的Consumer對象。
這個接口很簡單不需要什麼解釋,看源碼一眼OK。
@FunctionalInterface
public interface Consumer{
void accept(T t);
default ConsumerandThen(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
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
//新生成的function的返回值類型要after一樣
default
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
//
static
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>
Predicate,是一個條件判斷接口,接收一個T參數範圍boolean值,默認抽象方法test(t);and\\or\\negate分別對應邏輯與、或、非操作,isEqual。
@FunctionalInterface
public interface Predicate
boolean test(T t);
//邏輯與
default Predicate
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
//獲取該對象否定的Predicate,相當於逆轉boolean
default Predicate
return (t) -> !test(t);
}
//邏輯或
default Predicate
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
//生成一個判斷對象是否相等的Predicate
static
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>
Consumer
這裡的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
閱讀更多 我是OwenJia 的文章