泛型、回調思想的應用

版權聲明:本文爲博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/xsp_happyboy/article/details/81743503

1 場景

相信很多做電商的小夥伴們都會遇到這樣一個需求:查出所有的手機集合,然後按照手機的批次字段,對這個集合進行分組。

2 一般的實現方法

手機類:

public class MobilePhone {

    /**
     * 主鍵
     */
    private String id;

    /**
     * 手機名稱
     */
    private String phoneName;

    /**
     * 手機生產代號
     */
    private String phoneCode;

    /**
     * 品牌
     */
    private String brand;

    /**
     * 類別
     */
    private String category;

    /**
     * 批號
     */
    private String batchNo;

    /**
     * 此處省略get、set方法
     */
}

分組方法:

/**
 * 商品list,按照批號分組
 * @param mobilePhones 商品List
 * @return 分組後的商品
 */
public Map<String,List<MobilePhone>> groupBy(List<MobilePhone> mobilePhones){
    Map<String,List<MobilePhone>> phoneMap = new HashMap<>();
    for (MobilePhone mobilePhone : mobilePhones) {
        List<MobilePhone> phoneList = phoneMap.get(mobilePhone.getBatchNo());
        if(phoneList == null){
            phoneList = new ArrayList<>();
            phoneMap.put(mobilePhone.getBatchNo(),phoneList);
        }
        phoneList.add(mobilePhone);
    }
    return phoneMap;
}

順便說一下,Java8以後,Map 有一個computeIfAbsent方法,可以對以上方法進行簡化:

public Map<String,List<MobilePhone>> groupBy2(List<MobilePhone> mobilePhones){
        Map<String,List<MobilePhone>> phoneMap = new HashMap<>();
        for (MobilePhone mobilePhone : mobilePhones) {
            List<MobilePhone> phoneList = 
            phoneMap.computeIfAbsent(mobilePhone.getBatchNo(), k -> new ArrayList<>());
            phoneList.add(mobilePhone);
        }
        return phoneMap;
    }

3 對方法進行抽象

由於產品的需求會經常改變,譬如說,現在又要求所有的手機按照品牌進行分組?又譬如說,要求手機按照類別進行分組?再者,又要求給電腦進行分組?像上面那樣去實現,豈不是要寫N個實現方法呢?

下面咱們聊聊,怎麼對方法進行抽象。

抽象分析
圖中的標號1、2、3處都可以用範型直接進行抽象,問題是標號4處的方法名會經常變化,難以抽象。

猜想:可否在經常變化的方法名外面封裝一層固定不變的方法呢?標號4處可以用固定的方法名代替,但是不同的對象仍需要調用特定的方法名去獲得屬性值,因而可以採用回調函數的思想。

3.1 回調函數

簡單的聊一聊回調函數

回調的過程一般由三個角色參與:初始函數(一般稱之爲主函數)、中間函數、回調函數。

舉個生活中的例子進行理解:旅客去酒店住宿,怕早上睡過頭,需要酒店老闆提供叫醒服務(叫醒服務就是中間函數),但叫醒的方式(叫醒的方式就是回調函數)需由旅客指定,比如打電話叫醒,敲門叫醒,或者怕睡得太死潑冷水叫醒。

僞代碼示例:

//回調函數一,打電話叫醒
void phoneWake(Guest g);

//回調函數二,敲門叫醒
void knockWake(Guest g);

//回調函數三,潑冷水叫醒
void waterWake(Guest g);
//中間函數
void wakeService(Guest g,wakeMethod wake){
    //中間函數調用回調函數
    wake(g);
}
//初始函數(主函數)
main(){
    //初始函數中調用中間函數,採用潑冷水的方式叫醒
    wakeService(g,waterWake);
}

3.2 具體實現

回到之前的問題,標號4處代碼:mobilePhone.getBatchNo(),由於屬性名不同getBatchNo會經常變化,需要在方法的外面再封裝一層名稱固定不變的方法。

Java8中有已經定義好的函數接口咱們可以直接拿來用!

@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);
}

Function中的apply方法就是咱們需要的固定不變的方法名,T就是實例中的MobilePhoneR就是getBatch()返回的String類型。

在調用groupBy 通用方法的時候,根據場景的不同,去實現Function 函數接口的R apply(T t) 方法,並把它作爲參數傳進來。例如:

groupBy(phones, new Function<MobilePhone, String>() {
            public String apply(MobilePhone mobilePhone){
                return mobilePhone.getBatchNo();
            }
        });

在Java8以後,推薦使用Lambda表達式代替匿名內部類

Talk is cheap, show me the code!!!

/**
 * 數據分組的通用方法
 * @param list list數據集合
 * @param action 函數接口
 * @param <T> 分組屬性值
 * @param <K> 分組數據
 * @return 分組結果
 */
public static <T,K> Map<T,List<K>> groupBy(List<K> list,Function<K,T> action){
    Map<T,List<K>> map = new HashMap<>();
    for (K k : list) {
        List<K> groupList = map.get(action.apply(k));
        if(groupList == null){
            groupList = new ArrayList<>();
            map.put(action.apply(k),groupList);
        }
        groupList.add(k);
    }
    return map;
}

測試用例:

public static void main(String args[]){

    // 這裏就假裝從數據庫中查出了數據哇
    List<MobilePhone> phones = new ArrayList<>();
    // 利用lambda表達式去實現Function函數接口
    Map<String,List<MobilePhone>> groupPhone =
            ListUtil.groupBy(phones, e -> e.getBatchNo());

    // 下面是lambda表達式的另一種寫法
    // 編譯器會把getBatchNo方法的調用者當作參數傳給apply方法,然後得到其方法的返回值
    // Map<String,List<MobilePhone>> groupPhone =
    // ListUtil.groupBy(phones, MobilePhone::getBatchNo);

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