本文博主純手敲,轉載請註明出處。java
因爲最近項目中都是基於JDK8或JDK9,所以熟悉新版本中的特性尤爲重要,這裏主要講解Java8中java.util.function下的的函數式接口聲明。bash
談到函數式接口,那不得不提的必然是@FunctionalInterface註解啦,望文知義,這個註解就是用在接口上的,被稱爲「功能接口」,那麼什麼是所謂的「功能接口」呢?又有哪些特性呢?下面一一道來。app
功能函數就是有@FunctionInterface註解的接口,特性包括:ide
一、 接口中只能有一個抽象方法,可是能夠有默認方法(被default修飾的方法),默認方法不是抽象方法,包含具體的方法體。這種設計主要是由於,對於功能接口來說,一個接口只會提供一個邏輯上的方法,經過這個邏輯上的方法表達一個單一函數,單是指多個方法的其餘方法須要是繼承自Object的public方法,或者你要想繞過,就本身實現default。函數式接口本身自己必定是隻有一個抽象方法。同時,若是是Object類的public方法,也是不容許的。官方的說明翻譯以下: 若是一個接口I,I有一組抽象方法集合M,且這些方法都不是Object類的public簽名方法,那麼若是存在一個M中的方法m,知足:函數
m的簽名是全部M中方法簽名的子簽名。ui
m對於M中的每一個方法都是返回類型可替換的。 個人理解是說一個功能函數只能有一個實際方法的「映射」,下面舉個例子說明:this
首先,在一個功能接口中有且僅有一個非Object中的抽象方法spa
/**
* 有且僅有一個,並且是非Object裏的方法
*/
@FunctionalInterface
public interface InterfaceTest {
boolean equals(Object obj);//會報錯,由於equals是Object的方法
}
//下面這個就沒問題了,由於有一個抽象方法,另一個是Object的,能夠共存
@FunctionalInterface
public interface InterfaceTest {
boolean equals(Object obj);
void foo();
}
//下面這個就又不行了,由於必須是Object的public方法才能夠,clone是protected的
@FunctionInterface
public interface InterfaceTest(){
Object clone();
void foo();
}
複製代碼
以上總結爲一句話就是:函數式接口,有且僅有一個抽象方法,Object的public方法除外。.net
下面來看一下Function接口中的方法: Function接口只是提供了一個apply方法,傳入E返回R,源碼以下:翻譯
@FunctionalInterface
public interface Function<T, R> {
/**
* Applies this function to the given argument.
*
* @param t the function argument
* @return the function result
*/
R apply(T t);
}
複製代碼
那麼提供一個這種抽象方法有什麼意義呢?個人理解是能夠把它看作咱們實際方法的一種「映射」,這又是什麼意思呢?看例子:
public class UserDao(){
public User getUser(Integer userId){
User user = getUSer(userId);//getUser的方法實現不用考慮,這裏只是爲了說明問題
return user;
}
}
複製代碼
咱們看看這個例子中的getUser
方法是否是能夠看作是Function中的applay方法的一個實現?傳入的一種類型的參數,返回不一樣類型的結果。其實參數的類型和返回值類型也能夠是相同的,即T能夠與R相同。這裏不說它是實現,我更喜歡稱做它爲「映射」。
那麼有了這種映射,咱們在調用這個getUser方法的時候就很方便了,可使用lambda表達式,也可使用方法引用了,由於lambda表達式和方法引用使用的前提就是必須有該方法對應的功能函數方法映射,哈哈,仍是以爲映射比較合適。看下面例子:
import java.util.function.Function;
public class UserService {
public static void main(String[] args) {
UserService service = new UserService();
//lambda表達式寫法
User lamUser = service.getUser(id->new User(id,"name"+id,"123"),1);
System.out.println(lamUser);
//函數引用寫法
User user = service.getUser(UserDao::getUser, 1);
System.out.println(user);
}
public User getUser(Function<Integer, ? extends User> func,Integer id){
User user = func.apply(id);
return user;
}
}
複製代碼
怎麼樣?到了這裏是否是有點感受了?嘿嘿,接着向下看,這纔剛入門。。。
下面咱們將看看Java8裏java.util.function
包下的函數式接口。其中包括Function
、Predicate
、Supplier
、Consumer
、Operator
(沒有Operator接口,只有相似BinaryOperator這樣的接口)。
Function
Function的apply
方法前邊咱們已經看到過了,這裏再貼一遍:
@FunctionalInterface
public interface Function<T, R> {
/**
* Applies this function to the given argument.
*
* @param t the function argument
* @return the function result
*/
R apply(T t);
}
複製代碼
函數意爲將參數T傳遞給一個函數,返回R。即
其默認實現了3個default方法,分別是compose
、andThen
和identity
,對應的函數表達爲:compose
對應,體現嵌套關係;andThen
對應,轉換了嵌套的順序;還有identity
對應了一個傳遞自身的函數調用對應。從這裏看出來,compose
和andThen
對於兩個函數f和g來講,f.compose(g)
等價於g.andThen(f)
。若是這裏的關係式看的有點暈,那就解釋一下:
Compose
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
複製代碼
就是該方法中須要傳入一個Function做爲參數,在調用compose
方法時,先調用一下傳入的Function的apply
方法,而後再調用本函數內的apply
方法。
andThen
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
複製代碼
相信這個不用說也明白了,就是先調用傳入的Function的apply
,再調用本接口中的applay
方法。
identity
static <T> Function<T, T> identity() {
return t -> t;
}
複製代碼
這個方法就是一個靜態方法,返回一個執行了apply()方法以後只會返回輸入參數的函數對象。
看個例子吧:
public static void main(String[] args) {
Function<Integer, Integer> name = e -> e * 2;
Function<Integer, Integer> square = e -> e * e;
int value = name.andThen(square).apply(3);
System.out.println("andThen value=" + value);
int value2 = name.compose(square).apply(3);
System.out.println("compose value2=" + value2);
Object identity = Function.identity().apply("huohuo");
System.out.println(identity);
}
複製代碼
限於篇幅,本文先講到這裏,下一篇咱們一塊兒來看看高階函數。