Android中泛型在實際項目中的使用小結


前言

爲什麼要總結泛型的使用?泛型在項目中是如何體現價值的?不多說,總結一波。從實踐中到理論,最後迴歸泛型本質。

1.什麼是泛型?爲什麼要用泛型?

定義:

泛型:就是“寬泛的數據類型”,任意的數據類型。

作用:

  • 泛型可以解決數據類型的安全問題,它的主要原理是:在類聲明的時候通過一個標識表示類中某個屬性的類型或者是某個方法的返回值及參數類型

  • 在開發中常用用於代碼的抽象和封裝,使其工具化,通用化。可有效降低代碼的冗餘,使代碼的可讀性更高。

ArrayList源碼中, 可以發現泛型的使用到處存在。

public class ArrayList<E> extends AbstractList<E>
   
 public boolean addAll(int index, Collection<? extends E> c) {
        rangeCheckForAdd(index);
        boolean modified = false;
        for (E e : c) {
            add(index++, e);
            modified = true;
        }
        return modified;
    }

注意點:

  • 泛型通常用<>和一個大寫字母表示,而一般常用的就是 <T><?><? extends Object><? super Object>四種方式。

    • 其中,T表示同一種類型,表示任意類型,<? extends XX>表示xx類型,和xx類型的子<? super XX >表示xx類型,和xx類型的父類型。
    • class<T>:實例化的時候,需要指定具體類型。class<?>:可以表示所有類型。
  • 不建議使用<G><Q>這種奇葩定義方式。

  • 泛型不能使用基本數據類型,如int,double等,只能使用它們的容器類如IntegerDouble。因爲Java的泛型是用類型擦除實現的,在運行階段用的都是Object,而基本數據類型不是繼承自Object,也就無法在泛型中使用。

  • Kotlin 中的 outin

    和 Java 泛型一樣,Kolin 中的泛型本身也是不可變的。

    • 使用關鍵字 out 來支持協變,等同於 Java 中的上界通配符 ? extends

      Foo<? extends Bar>對應Foo<out Bar!>!

    • 使用關鍵字 in 來支持逆變,等同於 Java 中的下界通配符 ? super

      Foo<? supter Bar >對應Foo<in Bar!>!

2.Android中的使用場景

2.1 findViewById 的使用變化

 /**
     * FindViewById的泛型封裝,減少強轉代碼
     */
    public <T extends View> T findViewById(@IdRes int id) {
        return getDelegate().findViewById(id);
    }

//使用前:
 Button btnReload =(Button) findViewById(R.id.btn_reload);
//現在:
 Button btnReload =  findViewById(R.id.btn_reload);

經歷了Android版本的不停迭代,從最開始必須強制轉換,到後來內部已經實現了泛型的封裝就可以不用再強轉。當然,現在流行 Kotlin 後這一步都可以直接省略了。

2.2 BaseAdapter實現封裝的Adapter

ListView使用盛行的時候,我們一般會類似這樣封裝一個Adapter 。

public abstract class CommonAdapter<T> extends BaseAdapter {
    protected LayoutInflater mLayoutInflater;
    protected List<T> mDatas;
    protected Context mContext;

    public CommonAdapter(Context mContext) {
        this.mContext = mContext;
    }
    public CommonAdapter(List<T> mDatas, Context mContext) {
        this.mDatas = mDatas;
        this.mContext = mContext;
    }

    public CommonAdapter(LayoutInflater mLayoutInflater, List<T> mDatas, Context mContext) {
        this.mLayoutInflater = mLayoutInflater;
        this.mDatas = mDatas;
        this.mContext = mContext;
    }

    @Override
    public int getCount() {
        return mDatas.size();
    }

    @Override
    public T getItem(int position) {
        return mDatas.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public abstract View getView(int position, View convertView, ViewGroup parent);
}

RecycleView出世的時候,我們可以這樣用泛型,具體可以看看這篇文章中有關 RecycleView使用的總結,點擊前往

Adapter通常都需要接受一個數據或者集合,但是數據類型卻是不確定的,因而像這樣因爲要接受寬泛的數據類型的場景使用泛型是很自然而然的事情.通用性就是泛型的一大好處。

2.3 網絡請求數據

開發中,網絡請求數據時返回的 json數據一般長這樣的,比如返回一個用戶信息:

      "code": 0,
      "message": "success",
      "data": {
        "token": "pbGUiLCJuYmYiOjE1NzI4NTg0NjIsInN1YiI6IjMxNDQifQ.YJ2A1wl2Jo9hbyRfkMlthoMkhfuKtlZh0vrkgi-rPpw",
        "uid": "a41b3bb3e6b050b6c915",
        "phone": "13908213909",
        "name": "小羊子說",
        "shopName": "成都銀泰城",
        "authTrueName": 1
      }
    }

或者返回一個分組信息:

 	  "code": 0,
      "message": "成功",
      "data": {
        "groups": [
          {
            "name": "我愛你中國",
            "count": 12,
            "groupId": 116
          },      
    
          {
            "name": "富二代",
            "count": 1,
            "groupId": 97
          },
          {
            "name": "高富帥",
            "count": 2,
            "groupId": 96
          },
          {
            "name": "未定義的分組",
            "count": 21886,
            "groupId": -1
          }
        ],
        "linkman": "大貴貴"
      }
    }
        

其中codemessagedata三個字段中的key值是不變的,可以理解爲共同的基礎數據不變,不管服務器返回什麼數據,這些都是不變的。

只有data裏面的數據是不同的,可以理解爲需求數據不同。

找到共同的返回信息後,我們就可以開始泛化封裝了。BaseBean就是對基礎數據進行封裝處理。

首先封裝通用的BaseBean:

public class BaseBean<T> {
    public int code;
    public String message;
    public T data;

    @Override
    public String toString() {
        return "code: " + code +
                "\nmessage: " + message +
                "\ndata: " + data;
    }
}

在使用時就方便多了。


//如檢測版本信息中的實現類
public class VersionBean extends BaseBean<VersionBean.DataBean> {

    public static class DataBean {
        public String appname; //App應用名
        public String detailhtml; //更新詳情
        public String downloadurl;//渠道下載地址
        public String downloadurl_common; //通用下載地址 備用
        public String detail; //更新詳情
        public String updatetime;
        public String version; //版本名 如版本“1.1.2”
        public int versionCode;//版本號 每次升級
        public int isforceupdate; //是否強制升級 如  0正常升級 1 強制升級

    }
}

//如多個關鍵字返回的列表中的實體類
public class KeywordBean extends BaseBean<List<KeywordBean.KeyWord>> {
    public static class KeyWord {
        public int id;
        public String title;
    }
}

同時結合RxJava+Retrofit請求接口修改返回的數據類型:

    @GET("getAppVersion.php")
    Observable<VersionBean> checkVersion(@Query("deviceType") int deviceType,
                                         @Query("channel") String channel,
                                         @Query("versionCode") int versionCode);
    @GET("keyWords.php")
    Observable<KeywordBean> keyWord();

網絡請求中的泛型簡單示例就先介紹到這裏, 具體使用可以參考我之前的一篇總結,點擊前往

2.4 其他應用場景

比如開發中採用了MVP架構,也會用到。熟悉運用泛型尤爲重要。

如何使用泛型和抽象優化MVP 的結構就變成了我們用好MVP的關鍵了。當然,對於這個問題我們可以通過泛型參數、抽象父類的方式,將一些公用的Model及Presenter抽象出來。這應該就是使用MVP架構的精髓了。

3.總結

泛型的使用 在開發運用十分廣泛,後期會不斷更新。

擴展閱讀:

1.碼上開學-Kotlin 的泛型

2.Google官方MVP架構解析

發佈了50 篇原創文章 · 獲贊 192 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章