Android、Java泛型掃盲

首先咱們定義A、B、C、D四個類,他們的關係以下java

class A {}
class B extends A {}
class C extends B {}
class D extends C {}
複製代碼

不指明泛型類型

//如下代碼均編譯經過
List list = new ArrayList();
//不指明泛型類型,泛型默認爲Object類型,故能往裏面添加任意實例對象
list.add(new A());
list.add(new B());
list.add(new C());
//取出則默認爲Object類型
Object o = list.get(0);
複製代碼

這個好理解,由於全部的類都繼承與Object,故能往list裏面添加任意實例對象數組

無邊界通配符

首先咱們要明白一個概念,通配符意義就是它是一個未知的符號,能夠是表明任意的類。數據結構

//咱們發現,這樣寫編譯不經過,緣由很簡單,泛型不匹配,雖然B繼承A
List<A> listA = new ArrayList<B>(); 

//如下5行代碼均編譯經過
List<?> list;
list = new ArrayList<A>();
list = new ArrayList<B>();
list = new ArrayList<C>();
list = new ArrayList<D>();

Object o = list.get(0); //編譯經過
list.add(new A());      //編譯不經過
list.add(new B());      //編譯不經過
list.add(new C());      //編譯不經過
list.add(new D());      //編譯不經過
複製代碼

知識點post

  • 無邊界通配符 能取不能存。這個好理解,由於編譯器不知道?具體是啥類型,故不能存;可是任意類型都繼承於Object,故能取,但取出默認爲Object對象。

上邊界符 ?extends

繼續上代碼this

List<? extends C> listC;
listC = new ArrayList<A>(); //編譯不經過
listC = new ArrayList<B>(); //編譯不經過
listC = new ArrayList<C>(); //編譯經過
listC = new ArrayList<D>(); //編譯經過

C c = listC.get(0); //編譯經過
listC.add(new C()); //編譯不經過
listC.add(new D()); //編譯不經過
複製代碼

知識點:spa

  1. 上邊界符 ? extends 只是限定了賦值給它的實例類型(這裏爲賦值給listC的實例類型),且邊界包括自身。
  2. 上邊界符 ? extends 同樣能取不能存,道理是同樣的,雖然限定了上邊界,但編譯器依然不知道 ? 是啥類型,故不能存;可是限定了上邊界,故取出來的對象類型默認爲上邊界的類型

下邊界符 ?super

List<? super B> listB;
listB = new ArrayList<A>(); //編譯經過
listB = new ArrayList<B>(); //編譯經過
listB = new ArrayList<C>(); //編譯不經過
listB = new ArrayList<D>(); //編譯不經過

Object o = listB.get(0); //編譯經過
listB.add(new A()); //編譯不經過
listB.add(new B()); //編譯經過
listB.add(new C()); //編譯經過
listB.add(new D()); //編譯經過
複製代碼

知識點翻譯

  1. 下邊界符 ?super,跟上邊界符同樣,只是限定了賦值給它的實例類型,也包括邊界自身
  2. 下邊界符 ?super 能存能取,由於設定了下邊界,故咱們能存下邊界如下的類型,固然也包括邊界自身;然而取得時候編譯器依然不知道 ? 具體是什麼類型,故取出默認爲Object類型。

類型擦除

首先咱們要明白一點:Java 的泛型在編譯期有效,在運行期會被刪除 咱們來看一段代碼code

//這兩個方法寫在同一個類裏
public void list(List<A> listA) {}  
public void list(List<B> listB) {} 
複製代碼

上面的代碼會有問題嗎?顯然是有的,編譯器報錯,提示以下信息: list(List<A>) clashed with list(List<B>) ; both methods have same erasure 翻譯過來就是,在類型擦除後,兩個方法具備相同的簽名,咱們來看看類型擦除後是什麼樣子cdn

public void list(List listA) {}  
public void list(List listB) {} 
複製代碼

能夠看出,兩個方法簽名徹底一致,故編譯不經過。 明白了類型擦除,咱們還須要明白一個概念對象

  • 泛型類並無本身獨有的Class類對象

好比並不存在List<A>.class或是List<B>.class,而只有List.class 接下來這個案例就好理解了

List<A> listA = new ArrayList<A>();
List<B> listB = new ArrayList<B>();
System.out.println(listA.getClass() == listB.getClass());  //輸出true
複製代碼

泛型傳遞

現實開發中,咱們常常會用到泛型傳遞,例如咱們常常須要對Http請求返回的結果作反序列化操做

public static <T> T fromJson(String result, Class<T> type) {
    try {
        return new Gson().fromJson(result, type);
    } catch (Exception ignore) {
        return null;
    }
}
複製代碼

此時咱們傳進去是什麼類型,就會返回自動該類型的對象

String result="xxx";
A a = fromJson(result, A.class);
B b = fromJson(result, B.class);
C c = fromJson(result, C.class);
D d = fromJson(result, D.class);
Integer integer = fromJson(result, Integer.class);
String str = fromJson(result, String.class);
Boolean boo = fromJson(result, Boolean.class);
複製代碼

那若是咱們想返回一個集合呢,如List<A>,下面這樣明顯是不對的。

//編譯報錯,前面類型擦除時,咱們講過,不存List<A>.class這種類型
ArrayList<A> list = fromJson(result, ArrayList<A>.class);
複製代碼

那咱們該怎麼作呢?首先,咱們對fromJson改造一下,以下:

//type爲一個數組類型
public static <T> List<T> fromJson(String result, Class<T[]> type) {
    try {
        T[] arr = new Gson().fromJson(result, type);//首先拿到數組
        return Arrays.asList(arr); //數組轉集合
    } catch (Exception ignore) {
        return null;
    }
}
複製代碼

這個時候咱們就能夠這麼作了

String result="xxx";
List<A> listA = fromJson(result, A[].class);
List<B> listB = fromJson(result, B[].class);
List<C> listC = fromJson(result, C[].class);
List<D> listD = fromJson(result, D[].class);
List<Integer> listInt = fromJson(result, Integer[].class);
List<String> listStr = fromJson(result, String[].class);
List<Boolean> listBoo = fromJson(result, Boolean[].class);
複製代碼

ok,我在再來,相信大多數Http接口返回的數據格式是這樣的:

public class Response<T> {
    private T data;
    private int code;
    private String msg;
    //省略get/set方法
}
複製代碼

那這種咱們又該如何傳遞呢?顯然用前面的兩個fromJson方法都行不通,咱們再來改造一下,以下:

//這裏咱們直接傳遞一個Type類型
public static <T> T fromJson(String result, Type type) {
    try {
        return new Gson().fromJson(result, type);
    } catch (Exception ignore) {
        return null;
    }
}
複製代碼

這個Type是什麼鬼?點進去看看

public interface Type {
    default String getTypeName() {
        return toString();
    }
}
複製代碼

哦,原來就是一個接口,而且只有一個方法,咱們再來看看它的實現類

在這裏插入圖片描述
發現有5個實現類,其中4個是接口,另一個是Class類,咱們再來看看Class類的聲明

public final class Class<T> implements java.io.Serializable, GenericDeclaration, Type, AnnotatedElement {
                              //省略內部代碼
}
複製代碼

如今有沒有明白點,如今咱們重點來關注下Type接口的其中一個實現接口ParameterizedType,咱們來看下它的內部代碼,裏面就只有3個方法

public interface ParameterizedType extends Type {
    /** * 例如: * List<String> list; 則返回 {String.class} * Map<String,Long> map; 則返回 {String.class,Long.class} * Map.Entry<String,Long> entry; 則返回 {String.class,Long.class} * * @return 以數組的形式返回全部的泛型類型 */
    Type[] getActualTypeArguments();

    /** * 例如: * List<String> list; 則返回 List.class * Map<String,Long> map; 則返回 Map.class * Map.Entry<String,Long> entry; 則返回 Entry.class * * @return 返回泛型類的真實類型 */
    Type getRawType();

    /** * 例如: * List<String> list; 則返回 null * Map<String,Long> map; 則返回 null * Map.Entry<String,Long> entry; 則返回 Map.class * * @return 返回泛型類持有者的類型,這裏能夠簡單理解爲返回外部類的類型,若是沒有外部類,則返回null */
    Type getOwnerType();
}
複製代碼

顧名思義,ParameterizedType 表明一個參數化類型。

這個時候咱們來自定義一個類,並實現ParameterizedType接口,以下:

public class ParameterizedTypeImpl implements ParameterizedType {

    private Type rawType;//真實類型
    private Type actualType;//泛型類型

    public ParameterizedTypeImpl(Type rawType,Type actualType) {
        this.rawType = rawType;
        this.actualType = actualType;
    }

    public Type[] getActualTypeArguments() {
        return new Type[]{actualType};
    }

    public Type getRawType() {
        return rawType;
    }

    public Type getOwnerType() {
        return null;
    }
}
複製代碼

咱們再次貼出fromJson方法

//這裏咱們直接傳遞一個Type類型
public static <T> T fromJson(String result, Type type) {
    try {
        return new Gson().fromJson(result, type);
    } catch (Exception ignore) {
        return null;
    }
}
複製代碼

此時咱們想獲得Response<T>對象,就能夠這樣寫

Response<A> responseA = fromJson(result, new ParameterizedTypeImpl(Response.class, A.class));
Response<B> responseB = fromJson(result, new ParameterizedTypeImpl(Response.class, B.class));
Response<C> responseC = fromJson(result, new ParameterizedTypeImpl(Response.class, C.class));
複製代碼

想獲得List<T>對象,也能夠經過ParameterizedTypeImpl獲得,以下:

List<A> listA = fromJson(result, new ParameterizedTypeImpl(List.class, A.class));
List<B> listB = fromJson(result, new ParameterizedTypeImpl(List.class, B.class));
List<C> listC = fromJson(result, new ParameterizedTypeImpl(List.class, C.class));
複製代碼

然而,若是咱們想獲得Response<List<T>>對象,又該如何獲得呢? ParameterizedTypeImpl同樣可以實現,以下:

//第一步,建立List<T>對象對應的Type類型
Type listAType = new ParameterizedTypeImpl(List.class, A.class);
Type listBType = new ParameterizedTypeImpl(List.class, B.class);
Type listCType = new ParameterizedTypeImpl(List.class, C.class);

//第二步,建立Response<List<T>>對象對應的Type類型
Type responseListAType = new ParameterizedTypeImpl(Response.class, listAType);
Type responseListBType = new ParameterizedTypeImpl(Response.class, listBType);
Type responseListCType = new ParameterizedTypeImpl(Response.class, listCType);

//第三步,經過Type對象,獲取對應的Response<List<T>>對象
Response<List<A>> responseListA = fromJson(result, responseListAType);
Response<List<B>> responseListB = fromJson(result, responseListBType);
Response<List<C>> responseListC = fromJson(result, responseListCType);

複製代碼

而後,能不能再簡單一點呢?能夠,咱們對ParameterizedTypeImpl改造一下

/** * User: ljx * Date: 2018/10/23 * Time: 09:36 */
public class ParameterizedTypeImpl implements ParameterizedType {

    private final Type   rawType;
    private final Type   ownerType;
    private final Type[] actualTypeArguments;

    //適用於單個泛型參數的類
    public ParameterizedTypeImpl(Type rawType, Type actualType) {
        this(null, rawType, actualType);
    }

    //適用於多個泛型參數的類
    public ParameterizedTypeImpl(Type ownerType, Type rawType, Type... actualTypeArguments) {
        this.rawType = rawType;
        this.ownerType = ownerType;
        this.actualTypeArguments = actualTypeArguments;
    }

    /** * 本方法僅使用於單個泛型參數的類 * 根據types數組,肯定具體的泛型類型 * List<List<String>> 對應 get(List.class, List.class, String.class) * * @param types Type數組 * @return ParameterizedTypeImpl */
    public static ParameterizedTypeImpl get(@NonNull Type rawType, @NonNull Type... types) {
        final int length = types.length;
        if (length > 1) {
            Type parameterizedType = new ParameterizedTypeImpl(types[length - 2], types[length - 1]);
            Type[] newTypes = Arrays.copyOf(types, length - 1);
            newTypes[newTypes.length - 1] = parameterizedType;
            return get(rawType, newTypes);
        }
        return new ParameterizedTypeImpl(rawType, types[0]);
    }

    //適用於多個泛型參數的類
    public static ParameterizedTypeImpl getParameterized(@NonNull Type rawType, @NonNull Type... actualTypeArguments) {
        return new ParameterizedTypeImpl(null, rawType, actualTypeArguments);
    }

    public final Type[] getActualTypeArguments() {
        return actualTypeArguments;
    }

    public final Type getOwnerType() {
        return ownerType;
    }

    public final Type getRawType() {
        return rawType;
    }

}
複製代碼

此時,咱們就能夠這樣寫

//第一步,直接建立Response<List<T>>對象對應的Type類型
Type responseListAType = ParameterizedTypeImpl.get(Response.class, List.class, A.class);
Type responseListBType = ParameterizedTypeImpl.get(Response.class, List.class, B.class)
Type responseListCType = ParameterizedTypeImpl.get(Response.class, List.class, C.class)

//第二步,經過Type對象,獲取對應的Response<List<T>>對象
Response<List<A>> responseListA = fromJson(result, responseListAType);
Response<List<B>> responseListB = fromJson(result, responseListBType);
Response<List<C>> responseListC = fromJson(result, responseListCType);
複製代碼

現實開發中,咱們還可能遇到這樣的數據結構

{
    "code": 0,
    "msg": "",
    "data": {
        "totalPage": 0,
        "list": []
    }
}
複製代碼

此時,Response<T> 裏面的泛型傳List確定是不能正常解析的,咱們須要再定一個類

public class PageList<T>{
   private int totalPage;
   private List<T> list;
   //省略get/set方法
}
複製代碼

此時就能夠這樣解析數據

//第一步,直接建立Response<PageList<T>>對象對應的Type類型
Type responsePageListAType = ParameterizedTypeImpl.get(Response.class, PageList.class, A.class);
Type responsePageListBType = ParameterizedTypeImpl.get(Response.class, PageList.class, B.class)
Type responsePageListCType = ParameterizedTypeImpl.get(Response.class, PageList.class, C.class)

//第二步,經過Type對象,獲取對應的Response<PageList<T>>對象
Response<PageList<A>> responsePageListA = fromJson(result, responsePageListAType);
Response<PageList<B>> responsePageListB = fromJson(result, responsePageListBType);
Response<PageList<C>> responsePageListC = fromJson(result, responsePageListCType);
複製代碼

注:ParameterizedTypeImpl get(Type... types)僅僅適用於單個泛型參數的時候,如Map等,有兩個泛型參數以上的不要用此方法獲取Type類型。若是須要獲取Map等兩個泛型參數以上的Type類型。可調用getParameterized(@NonNull Type rawType, @NonNull Type... actualTypeArguments)構造方法獲取,如:

//獲取 Map<String,String> 對應的Type類型
Type mapType = ParameterizedTypeImpl.getParameterized(Map.class, String.classs, String.class)

//獲取 Map<A,B> 對應的Type類型
Type mapType = ParameterizedTypeImpl.getParameterized(Map.class, A.classs, B.class)
複製代碼

到這,泛型相關知識點講解完畢,若有疑問,請留言。

感興趣的同窗,能夠查看個人另外一片文章RxHttp 一條鏈發送請求,新一代Http請求神器(一))裏面就用到了ParameterizedTypeImpl類進行泛型傳遞。

相關文章
相關標籤/搜索