提升java業務代碼優雅度之函數式接口編程

如何提高java代碼優雅度,java8中函數式編程是不得不提的一個重要模塊。下面直奔主題:

一.函數式接口的定義

  1. 被@FunctionalInterface註釋的接口,滿足@FunctionalInterface註釋的約束;

  2. 沒有被@FunctionalInterface註釋的接口,但是滿足@FunctionalInterface註釋的約束。

二.@FunctionalInterface註釋的約束

  1. 接口有且只能有個一個抽象方法,只有方法定義,沒有方法體;
  2. 在接口中覆寫Object類中的public方法,不算是函數式接口的方法。

示例:

/**
 *
 * @author zhaoj
 * @version FunctionInterfaceTest.java, v 0.1 2019-09-04 17:53
 */
@FunctionalInterface
public interface FunctionInterfaceTest {
    /**
     * 自定義方法
     * @param input
     * @return
     */
    String fetch(String input);

    /**
     * Object中的方法
     */

    @Override
    String toString();

    /**
     * Object中的方法
     * @param obj
     * @return
     */
    @Override
    boolean equals(Object obj);
}

 

三.函數式接口實例的創建

函數式接口實例的創建有三種方式:

1、lambda表達式;

2、方法引用;

3、構造方法引用。

三種示例:

/**
 *
 * @author zhaoj
 * @version FunctionalInterfaceTest0.java, v 0.1 2019-09-04 17:51
 */
public class FunctionalInterfaceTest0 {
    public static void main(String[] args) {

        /**
         * 1、lambda表達式
         */
        FunctionInterfaceTest functionInterfaceTest1 = item -> item;

        /**
         * 2、方法引用
         */
        FunctionInterfaceTest functionInterfaceTest2 = FunctionalInterfaceTest0::getInstance;

        FunctionInterfaceTest functionInterfaceTest3 = FunctionalInterfaceTest0::getMessage;
        
        String msg1 = join("hello",functionInterfaceTest2);
        System.out.println(msg1);

        String msg2 = join("hello",functionInterfaceTest3);
        System.out.println(msg2);

        /**
         * 3、構造方法引用
         */
        FunctionInterfaceTest functionInterfaceTest4 = String::new;
    }

    public static String getInstance(String item){
        return item+"!world";
    }

    public static String getMessage(String massage){
        return "world,"+ massage+"!";
    }

    public  static String join(String str,FunctionInterfaceTest functionTest){
        return functionTest.fetch(str);
    }

四.java8中常用的函數式接口

常用的函數式接口主要有四種類型,是通過其輸入和輸出的參數來進行區分的:

接口名

說明

Function<T,R> 

接收一個T類型的參數,返回一個R類型的結果

Consumer<T>

接收一個T類型的參數,不返回值

Predicate<T>

接收一個T類型的參數,返回一個boolean類型的結果

Supplier<T>

不接受參數,返回一個T類型的結果

示例:

/**
 * @author zhaoj
 * @version FunctionalInterfaceTest1.java, v 0.1 2019-09-05 9:34
 */
public class FunctionalInterfaceTest1 {
    public static void main(String[] args) {
        //1.第一種創建方式 接收一個T類型的參數,返回一個R類型的結果
        Function<String, String> function1 = item -> item + "返回值";

        //第二種創建方式,接收一個T類型的參數,不返回值,lambda語句,使用大括號,沒有return關鍵字,表示沒有返回值
        Consumer<String> function2 = item -> {
            System.out.println(item);
        };

        //第三種創建方式,接收一個T類型的參數,返回一個boolean類型的結果
        PredicateTest<String> function3 = item -> "".equals(item);

        //第四種創建方式,不接受參數,返回一個T類型的結果
        Supplier<String> function4 = () -> new String("123");

        /**
         * 1、創建一個String類型的集合
         * 2、將集合中的所有元素的末尾追加字符串'1'
         * 3、選出長度大於2的字符串
         * 4、遍歷輸出所有元素
         */
        List<String> list = Arrays.asList("zhangsan", "lisi", "wangwu", "xiaoming", "zhaoliu");

        list.stream()
                //傳入的是一個Function函數式接口
                .map(value -> value + "666")
                //傳入的是一個Predicate函數式接口
                .filter(value -> value.length() > 5)
                //傳入的是一個Consumer函數式接口
                .forEach(value -> {
                    System.out.println(value);
                });

    }
}

Java8中對於接收兩個參數的場景提供了相關的函數式接口:

接口名

說明

BiFunction<T, U, R>  

 接收T類型和U類型的兩個參數,返回一個R類型的結果

BiConsumer<T , U>

接收T類型和U類型的兩個參數,不返回值

BiPredicate<T, U>

接收T類型和U類型的兩個參數,返回一個boolean類型的結果

示例:

/**
 *
 * @author zhaoj
 * @version FunctionalInterfaceTest2.java, v 0.1 2019-09-05 17:43
 */
public class FunctionalInterfaceTest2 {
    public static void main(String[] args) {

        /**
         * Bi類型的接口創建
         */
        BiFunction<String, String, Integer> biFunction = (str1, str2) -> str1.length() + str2.length();

        BiConsumer<String, String> biConsumer = (str1, str2) -> System.out.println(str1 + str2);

        BiPredicate<String, String> biPredicate = (str1, str2) -> str1.length() > str2.length();

}
        /**
         * Bi類型的接口使用
         */
        //輸出10
        int length = getLength("hello", "world", (str1, str2) -> str1.length() + str2.length());
        //輸出false
        boolean boolean1 = getBoolean("hello", "world", (str1, str2) -> str1.length() > str2.length());

        System.out.println(length);
        System.out.println(boolean1);
       //沒有輸出
        noResult("hello", "world", (str1, str2) -> System.out.println(str1 + " " + str2));


    }

    public static int getLength(String str1, String str2, BiFunction<String, String, Integer> function) {
        return function.apply(str1, str2);
    }

    public static void noResult(String str1, String str2, BiConsumer<String, String> biConcumer) {
        biConcumer.accept(str1, str2);
    }

    public static boolean getBoolean(String str1, String str2, BiPredicate<String, String> biPredicate) {
        return biPredicate.test(str1, str2);
    }
}

關於多個參數值的使用,無論實在Function接口中,還是在BI類型的接口都提供了類似的操作。(注:java8中,接口的方法是可以有實現的,但需要default關鍵字修飾,這是其他版本的jdk沒有的特性)

1、  Function接口的andThen方法和compose方法

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));
}

解釋說明:

      Compose方法:方法接收一個Function類型的參數,返回一個值。這也是一個標準的Function類型的定義。在compose方法內部也有一個apply方法。在執行compose方法中的apply方法之前,它先執行了before接口的apply方法,也是compose方法的輸入參數。然後將before方法執行的返回值作爲compose中apply方法的輸入參數。

      andThen方法:該方法與compose方法很類似。不同之處在於,andThen是先執行自身的apply方法,將apply的返回值作爲after接口的輸入值。相對於compose方法,只是順序的不同。

2、Consumer接口的andThen方法

default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
}

解釋說明:

      將輸入參數分別賦給andThen內部的accept方法和after內部的accept方法。After的計算在andThen之後,起到了後置連接的作用。在這裏沒有compose方法,因爲後置連接反過來就是前置連接,所以不需要一個多餘的compose方法了。只需要在傳遞時,交換兩個consumer的順序即可。

3、 predicate接口的and、or、negate方法

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);
}

解釋說明:

       分別是與 ,非,或操作。

此外,java8針對原生類型int,long,double都提供了相應的函數式接口,使用方式都相同,見java.util.function包。

五.自定義使用

//自定義T,U兩個入參,無返回參數,用於構建組裝信息
@FunctionalInterface
public interface Builder<T, U> {

    void build(T t, U u);
}
//自定義T,U兩個入參,返回驗證結果
@FunctionalInterface
public interface Verification<T,U> {
    boolean verify(T t, U u);
}

//自定義工具類
public class Util {

    public static <T, U> List<T> buildParam(List<T> left, List<U> right, Verification<T, U> verifyer, Builder<T, U> builder) {

        if (left.isEmpty() || right.isEmpty()) {
            return left;
        }

        left.forEach(l -> right.forEach(r -> {
            if (verifyer.verify(l, r)) {
                builder.build(l, r);
                return;
            }
        }));
        return left;
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章