java8 新特性推出的 Lambda 表達式,即函數式程式設計,相信很多開發胸弟都會使用了,但是什麼是函數式程式設計呢?别問我,我也不知道标準的定義。其核心思想是:使用不可變值和函數,函數對一個值進行處理,映射成另一個值。
函數接口
java8之前接口類隻有方法的定義,沒有實作的,Java8對接口提供預設方法的新特性。一個接口類可以定義n個抽象方法,但如果有 @FunctionalInterface 注解修飾就不一樣了,該注釋會強制編譯檢查一個接口是否符合函數接口的标準。如果該注釋添加給一個枚舉類型、類或另一個注釋,或者接口包含不止一個抽象方法,編譯就會報錯。@FunctionalInterface 注解修飾的接口就是被定義成函數接口。
常用的函數接口
平時開發中常用的函數接口有無傳回值的Consumer,傳回值為Boolean的Predicate,把入參T映射成R傳回值的Function 和傳回執行個體對象的Supplier。接下來我們一起分析這四個函數接口類的源碼以及簡單的使用,先建立一個後面需要用到的實體類 Griez。
public class Griez implements Serializable {
private int age;
private String name;
public Griez(int age, String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public Griez setAge(int age) {
this.age = age;
return this;
}
public String getName() {
return name;
}
public Griez setName(String name) {
this.name = name;
return this;
}
}
複制
定義一個法國球星格子(我最喜歡的球員)類,為了簡單這裡隻有名字和年齡,他的能力值和薪資就不展示出來了,怕吓到大家。
Consumer
@FunctionalInterface
public interface Consumer<T> {
// 執行時需要調用傳入的實作
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
複制
Consumer函數接口有兩個方法,抽象方法accept需要調用者自己實作;andThen預設方法封裝了兩個Consumer執行順序的Consumer執行個體,先執行本執行個體的accept,再執行參數after的accept方法。
public static void main(String[] args) {
Consumer<Griez> consumer = griez -> System.out.println(griez.getName() + griez.age);
Consumer<Griez> afterConsumer = griez -> System.out.println(griez.getAge());
Consumer<Griez> then = consumer.andThen(afterConsumer);
then.accept(new Griez(29, "wolf"));
}
複制
第1,2行建立Consumer執行個體,并實作了accept方法。記得剛畢業出來面試有個面試官問我接口能不能建立執行個體,我給他的回答是:“你說呢?”。
Predicate
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
// 兩個條件必須滿足
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
// 傳說中的 邏輯非 判斷
default Predicate<T> negate() {
return (t) -> !test(t);
}
// 邏輯或判斷
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
// 對象判斷?
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}
複制
test 方法需要自定義邏輯,傳回一個boolean值,該函數接口是做判斷用途的。and 方法是兩個Predicate 邏輯與判斷;negate 方法是邏輯非判斷;or 方法是兩個 Predicate 邏輯或判斷;isEqual 方法是判斷targetRef是否與本執行個體相同。
public static void main(String[] args) {
Predicate<Griez> predicate= griez -> griez.getAge() > 18;
System.out.println("Griez已經成年了 : " + predicate.test(new Griez(29, "griez")));
}
複制
test傳回Boolean值。
Function
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
static <T> Function<T, T> identity() {
return t -> t;
}
}
複制
apply 方法接收一個T參數,然後一個R,該方法是類型映射作用。compose 方法是組裝了兩個Function執行順序行為Function對象,先執行參數的Function在執行本執行個體Function。andThen 跟 compose執行順序相反。identity 方法是傳回參數本身。
public static void main(String[] args) {
Function function = o -> new Griez(20, "Female");
System.out.println(function.apply(new Griez(29, "griez").getName()));
}
複制
一個大老爺進去變成一個年輕小姐姐出來,變态的程式員。
Supplier
@FunctionalInterface
public interface Supplier<T> {
T get();
}
複制
該函數接口看上去就比較寒顫了,隻有一個方法。get 方法作用如名字一個,伸手黨來的,擷取一個執行個體。
public static void main(String[] args) {
Supplier<Griez> supplier = () -> new Griez(29, "griez");
System.out.println(supplier.get());
}
複制
擷取一個Griez對象。