三種方式實現函數式編程

三種方式實現函數式編程

摘要

\quad 在Java8引入函數式編程以後,不少複雜的代碼能夠大大的簡化,甚至能夠簡化成一句話。這裏就主要經過一個例子,分析使用Lambda表達式、靜態方法以及實例方法簡化代碼的優缺點。express

目錄

(點擊可直接跳轉)
*1.前言
*2.使用Lambda表達式
*3.使用靜態方法
*4.使用實例方法
*5.總結

1.前言

\quad 經過這個例子,一方面能夠認識到,本身之前寫過的代碼可讀性是多麼的差,另外一方面能夠知道如何優雅的使用接口。之前使用接口的時候,只知道實現了這個接口,就表明這個類具備某種能力。可是經過這個例子,我看到了接口還能夠用在方法中。實現接口的時候,只須要在lambda表達式中提供對應的(輸入/返回值)類型便可。
編程

2.使用Lambda表達式

先看下面這段代碼:
將map中的k,v值分別用不一樣的鏈接符打印出來
public static void printWithComma(Map<String, String> map1, Map<String, String> map2) {
        for (Map.Entry<String, String> entry : map1.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            System.out.println(key + "," + value);
        }

        for (Map.Entry<String, String> entry : map2.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            System.out.println(key + "," + value);
        }
    }

    public static void printWithDash(Map<String, String> map1, Map<String, String> map2) {
        for (Map.Entry<String, String> entry : map1.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            System.out.println(key + "-" + value);
        }

        for (Map.Entry<String, String> entry : map2.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            System.out.println(key + "-" + value);
        }
    }

    public static void printWithColon(Map<String, String> map1, Map<String, String> map2) {
        for (Map.Entry<String, String> entry : map1.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            System.out.println(key + ":" + value);
        }

        for (Map.Entry<String, String> entry : map2.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            System.out.println(key + ":" + value);
        }
    }
    
複製代碼

看完後發現,裏面很是多的重複代碼,天然而然的想到,能夠將其中的公共代碼抽取出來。而且函數是輸入兩個map,獲得一個void,這符合BiConsumer接口設定。因此,能夠在抽取的公共方法中實現BiConsumer接口,實現代碼複用。
代碼以下:bash

public static void printWithConsumer(
         Map<String, String> map1,
         Map<String, String> map2,
         BiConsumer<String, String> consumer) {
     map1.forEach(consumer);
     map2.forEach(consumer);
 }
複製代碼

打印方法就能夠被簡化成一句話了:函數式編程

public static void printWithComma(Map<String, String> map1, Map<String, String> map2) {
        printWithConsumer(map1, map2, (s, s2) -> System.out.println(s+","+s2));
    }
複製代碼

3.使用靜態方法

\quad 一樣使用第二個例子,對第二種方法進行簡化。函數

public static void printWithDash(Map<String, String> map1, Map<String, String> map2) {
        printWithConsumer(map1, map2, RefactorToConsumer::staticPrintWithDash);
    }

    public static void staticPrintWithDash(String s1, String s2) {
        System.out.println(s1 + "-" + s2);
    }
複製代碼

\quad定義的靜態方法消耗兩個String,獲得一個void,符合BiCounsumer的約定,因此可使用靜態方法引用的方式簡化代碼。
\quad分析兩者優缺點:
1.使用靜態方法能夠給方法取名字,這樣更加直觀,更容易被理解。
2.使用靜態方法能夠在方法中寫較爲複雜的代碼,而Lambda中通常就是一兩句話。因此須要較多的代碼實現的時候,最好仍是在靜態方法中聲明。
ui

4.使用實例方法

\quad 爲了便於理解,這裏使用另一個簡單的例子: 對一個User類實現過濾
代碼以下:this

public class User {
   
    private final Integer id;
    private final String name;

    public User(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    public Integer getId() {
        return id;
    }

    public String getName() {
        return name;
    }
    
    // 過濾姓張的用戶
    public static List<User> filterZhangUsers(List<User> users) {
        List<User> list = new ArrayList<>();
        for (User user:users
             ) {
            if (user.name.startsWith("張")){
                list.add(user);
            }
        }
        return list;
    }
}
複製代碼

\quad 爲了減小排版,我這裏只寫了一個過濾器,可是能夠想象若是須要過濾姓王的,ID爲偶數的User...咱們就能夠將其抽取出來,造成一個公共的抽取方法。不管用前面的Lambda表達式也好,用靜態方法也能夠,實現Predicate接口便可。除此以外,咱們還發現這個斷定的過程其實就是User類型轉換爲boolean類型的過程,而剛好方法就是在User類中定義的,因此沒必要將方法定義成static類型,使用實例方法便可。
代碼以下:spa

public static List<User> filterZhangUsers(List<User> users) {
       return filter(users,User::filterNameStartWithZhang);
    }
    
    public boolean filterNameStartWithZhang(){
        return this.name.startsWith("張");
    }

    public static List<User> filter(List<User> users, Predicate<User> predicate) {
        List<User> results = new ArrayList<>();
        for (User user : users
        ) {
            if (predicate.test(user)) {
                results.add(user);
            }
        }
        return results;
    }
複製代碼

\quad因爲 filterNameStartWithZhang()方法是非靜態的,在方法形參表中看似是空的,實際上形參是(User this),符合Predicate的從某一類型到boolean的設定。
code

5.總結

總結:
\quad1.將大量的重複代碼進行重構,是一件很是有必要的事情。不只有形的減小代碼量,並且在無形中減小了出bug的機率,而且大大的增長了代碼的可讀性。
\quad 2.使用Lambda表達式須要注意,在表達式中引用的變量都須要是effectively final類型的,不然會報
「Variable used in lambda expression should be final or effectively final」。
接口

解決方法:
1.可使用Atomic類型的參數。2.能夠將參數聲明爲全局類型的。
詳細參考這篇文章:zhuanlan.zhihu.com/p/82921974

相關文章
相關標籤/搜索