1. 程式人生 > >Java函數語言程式設計整理

Java函數語言程式設計整理

Java函數語言程式設計的第一個作用是可以將匿名類改寫成函式式表示式,由系統自動判斷型別

我們先定義一個介面

public interface Goods {
    boolean test(int i);
}

傳統的匿名類寫法

public class Anonymous {
    public static void main(String[] args) {
        List<Goods> goodsList = new ArrayList<>();
        goodsList.add(new Goods(){
            @Override
public boolean test(int i) { return 5 - i > 0; } }); System.out.println(goodsList.get(0).test(3)); } }

執行結果:

true

使用lamdba表示式的寫法

public class AnonymousLambda {
    public static void main(String[] args) {
        List<Goods> goodsList = new 
ArrayList<>(); goodsList.add(i -> 5 - i > 0); System.out.println(goodsList.get(0).test(3)); } }

執行結果

true

像這種一個接口裡面只有一個方法的介面,我們可以稱之為函式介面,JDK中有很多這樣的介面,當然我們可以在該介面上打上@FunctionalInterface以表明該介面是一個函式介面

@FunctionalInterface
public interface Goods {
    boolean test(int 
i); }

我們先來看一下JDK中的Predicate介面,這是一個可以進行條件判斷的介面

我們先用傳統方式來看一下這麼幾個條件

  • 判斷傳入的字串的長度是否大於5
  • 判斷傳入的引數是否是偶數
  • 判斷數字是否大於10
public class PredicateOne {
    /**
     * 判斷長度大於5
     * @param judgeString
     * @return
     */
    public boolean judgeStringLength(String judgeString) {
        return judgeString.length() > 5;
    }

    /**
     * 判斷是否是偶數
     * @param judgeInteger
     * @return
     */
    public boolean judgeIntegerOdds(int judgeInteger) {
        return (judgeInteger & 1) == 0;
    }

    /**
     * 判斷數字是否大於10
     * @param num
     * @return
     */
    public boolean judgeSecialNumber(int num) {
        return num > 10;
    }

    public static void main(String[] args) {
        PredicateOne predicateOne = new PredicateOne();
        System.out.println(predicateOne.judgeStringLength("12345"));
        System.out.println(predicateOne.judgeIntegerOdds(12345));
        System.out.println(predicateOne.judgeSecialNumber(12345));
    }
}

執行結果

false
false
true

現在改成函數語言程式設計如下

public class PredicateTwo<T> {
    public boolean judgeConditionByFunction(T t, Predicate<T> predicate) {
        return predicate.test(t);
    }

    public static void main(String[] args) {
        PredicateTwo<Integer> integerPredicateTwo = new PredicateTwo<>();
        System.out.println(integerPredicateTwo.judgeConditionByFunction(12345,t -> String.valueOf(t).length() > 5));
        System.out.println(integerPredicateTwo.judgeConditionByFunction(12345,t -> (t & 1) == 0));
        System.out.println(integerPredicateTwo.judgeConditionByFunction(12345,t -> t > 10));
    }

執行結果

false
false
true

我們知道邏輯判斷中有與、或、非的比較,Predicate介面同樣具有這樣的功能,我們來看一下它的原始碼

@FunctionalInterface
public interface Predicate<T> {

    /**
     * 需要實現的比較方法
     *
     * @param t the input argument
     * @return {@code true} if the input argument matches the predicate,
     * otherwise {@code false}
     */
    boolean test(T t);

    /**
     * 邏輯與,類似於&&
     *
     * @param other a predicate that will be logically-ANDed with this
     *              predicate
     * @return a composed predicate that represents the short-circuiting logical
     * AND of this predicate and the {@code other} predicate
     * @throws NullPointerException if other is null
     */
    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }

    /**
     * 邏輯非,類似於!
     *
     * @return a predicate that represents the logical negation of this
     * predicate
     */
    default Predicate<T> negate() {
        return (t) -> !test(t);
    }

    /**
     * 邏輯或,類似於||
     *
     * @param other a predicate that will be logically-ORed with this
     *              predicate
     * @return a composed predicate that represents the short-circuiting logical
     * OR of this predicate and the {@code other} predicate
     * @throws NullPointerException if other is null
     */
    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }

    /**
     * 邏輯等,類似於equals()方法
     *
     * @param <T> the type of arguments to the predicate
     * @param targetRef the object reference with which to compare for equality,
     *               which may be {@code null}
     * @return a predicate that tests if two arguments are equal according
     * to {@link Objects#equals(Object, Object)}
     */
    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }
}

現在我們加上條件的交集判斷

public class PredicateThree<T> {
    /**
     * 兩個條件的與操作
     * @param t
     * @param predicate
     * @param predicate1
     * @return
     */
    public boolean judgeConditionByFunctionAnd(T t, Predicate<T> predicate,Predicate<T> predicate1) {
        return predicate.and(predicate1).test(t);
    }

    /**
     * 兩個條件的或操作
     * @param t
     * @param predicate
     * @param predicate1
     * @return
     */
    public boolean judgeConditionByFunctionOr(T t, Predicate<T> predicate,Predicate<T> predicate1) {
        return predicate.or(predicate1).test(t);
    }

    /**
     * 對一個條件進行取反
     * @param t
     * @param predicate
     * @return
     */
    public boolean judgeConditionByFunctionNegate(T t,Predicate<T> predicate) {
        return predicate.negate().test(t);
    }

    /**
     * 判斷兩個物件的值是否相等
     * @param t1
     * @param t2
     * @return
     */
    public boolean judgeConditionIsEquals(T t1,T t2) {
        return Predicate.isEqual(t1).test(t2);
    }

    public static void main(String[] args) {
        PredicateThree<Integer> integerPredicateThree = new PredicateThree<>();
        System.out.println(integerPredicateThree.judgeConditionByFunctionAnd(12345,t -> t > 10,t ->(t & 1) == 0));
        System.out.println(integerPredicateThree.judgeConditionByFunctionOr(12345,t -> t > 10,t ->(t & 1) == 0));
        System.out.println(integerPredicateThree.judgeConditionByFunctionNegate(12345,t -> t > 10));
        System.out.println(integerPredicateThree.judgeConditionIsEquals(123,123));
    }
}

執行結果

false
true
false
true

我們再來看一下JDK中的Consumer介面

 

----------------------------------------------------------------------------------

而另一個最主要的作用就是對集合的操作,從傳統程式設計到函數語言程式設計(lambda),我們先來看一個最簡單的例子

@Data
@AllArgsConstructor
public class Artist {
    private String name;
    private String homeTown;

    public boolean isFrom(String homeTown) {
        if (this.homeTown.equals(homeTown)) {
            return true;
        }
        return false;
    }
}

這是一個藝術家的簡單類,包含名字和家鄉。

現在我們來建立一個藝術家的集合,然後判定來自倫敦的藝術家有多少人

public class ComeFrom {
    public static void main(String[] args) {
        List<Artist> artists = new ArrayList<>();
        artists.add(new Artist("Martin","London"));
        artists.add(new Artist("Shirly","Beijing"));
        artists.add(new Artist("Dilon","London"));
        artists.add(new Artist("Dave","NewYork"));
        int count = 0;
        for (Artist artist : artists) {
            if (artist.isFrom("London")) {
                count++;
            }
        }
        System.out.println(count);
    }
}

執行結果:

2

現在我們來改造成函式式的程式設計

public class ComeFromStream {
    public static void main(String[] args) {
        List<Artist> artists = new ArrayList<>();
        artists.add(new Artist("Martin","London"));
        artists.add(new Artist("Shirly","Beijing"));
        artists.add(new Artist("Dilon","London"));
        artists.add(new Artist("Dave","NewYork"));
        long count = artists.stream().filter(artist -> artist.isFrom("London"))
                .count();
        System.out.println(count);
    }
}

執行結果

2

這裡第一種方式,我們稱為外部迭代;而第二種方式,我們稱為內部迭代,而stream是這種用函數語言程式設計方式在集合類上進行復雜操作的工具。

如果我們對程式碼稍作修改

public class ComeFromStream {
    public static void main(String[] args) {
        List<Artist> artists = new ArrayList<>();
        artists.add(new Artist("Martin","London"));
        artists.add(new Artist("Shirly","Beijing"));
        artists.add(new Artist("Dilon","London"));
        artists.add(new Artist("Dave","NewYork"));
        artists.stream().filter(artist -> {
            System.out.println(artist.getName());
            return artist.isFrom("London");
        });
    }
}

執行該端程式碼,沒有任何輸出。像這種只過濾不計數的,filter只刻畫出了Stream,但並沒有產生新的集合的方法,我們稱之為惰性求值方法;而像count這樣最終會從Stream產生值的方法叫做及早求值方法。

我們再把上述程式碼改回求值。

public class ComeFromStream {
    public static void main(String[] args) {
        List<Artist> artists = new ArrayList<>();
        artists.add(new Artist("Martin","London"));
        artists.add(new Artist("Shirly","Beijing"));
        artists.add(new Artist("Dilon","London"));
        artists.add(new Artist("Dave","NewYork"));
        long count = artists.stream().filter(artist -> {
            System.out.println(artist.getName());
            return artist.isFrom("London");
        }).count();
        System.out.println(count);
    }
}

執行結果

Martin
Shirly
Dilon
Dave
2

判斷一個操作是惰性求值還是及早求值很簡單:只需看它的返回值。如果返回值是Stream,那麼是惰性求值;如果返回值是另一個值或為空,那麼就是及早求值。整個過程和建造者模式有共通之處。建造者模式使用一系列操作設定屬性和配置,最後呼叫build方法,這時,物件