首先咱們定義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
? extends
只是限定了賦值給它的實例類型(這裏爲賦值給listC的實例類型),且邊界包括自身。? 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()); //編譯經過
複製代碼
知識點
翻譯
?super
,跟上邊界符同樣,只是限定了賦值給它的實例類型,也包括邊界自身?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) {}
複製代碼
能夠看出,兩個方法簽名徹底一致,故編譯不經過。 明白了類型擦除,咱們還須要明白一個概念對象
好比並不存在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();
}
}
複製代碼
哦,原來就是一個接口,而且只有一個方法,咱們再來看看它的實現類
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
類進行泛型傳遞。