想要封裝一個好用的網(wǎng)絡框架灌砖,肯定繞不過泛型這關(guān)。
最近遇到網(wǎng)絡請求相關(guān)問題泣特,網(wǎng)上也沒有和我相同的需求,只能自己動手了挑随,順便學習學習json
和泛型
状您,先學習泛型
[TOC]
個人認知:泛型的作用
為了讓錯誤提前到編譯期發(fā)現(xiàn),使程序更安全
參考《Effective Java》
第五章泛型
第23條
raw type
List<E>
對應的raw type
是List
兜挨,raw type
可以直接使用膏孟,但是我們不應該這么做,這只是為了兼容問題做的保留拌汇,如果使用raw type
就失掉了泛型在安全性和表述性方面的所有優(yōu)勢
Gson
的new Type(){}
就是這樣柒桑,不要去這樣使用,使用時明確類型:new Type<Object>(){}
指定泛型
使用raw type
和<Object>
的區(qū)別噪舀,(List
和List<Object>
的區(qū)別)前者逃避了類型檢查
{//代碼示例...
List<String> list = new ArrayList<String>();
add1(list, 42);
add2(list, 42);//Error: 不兼容的類型: List<String>無法轉(zhuǎn)換為List<Object>
}
void add1(List list, Object o) {
list.add(o);
}
void add2(List<Object> list, Object o) {
list.add(o);
}
無限制通配符
泛型Set<E>
的無限制通配符類型為Set<?>
讀作"某個類型的集合"魁淳。
通配符是類型安全的,raw type
類型不安全
void add2(List<?> list, Object o) {
list.add("");
/*Error:(26, 13) 錯誤: 對于add(String), 找不到合適的方法
方法 Collection.add(CAP#1)不適用
(參數(shù)不匹配; String無法轉(zhuǎn)換為CAP#1)*/
}
raw type
可以很容易的破壞集合的約束條件与倡,而通配符則會限制這種情況的發(fā)生(null除外)
界逛, 可以配合泛型方法generic method
和有限制的通配符類型bounded wildcard type
使用
快速回顧
-
Set<Object>
是個參數(shù)化類型,表示可以包含任何對象類型的一個集合 -
Set<?>
則是一個通配符類型纺座,表示只能包含某種未知對象類型的一個集合 -
Set
則是一個原生態(tài)類型raw type
息拜,它脫離了泛型系統(tǒng),不安全比驻。
- 參數(shù)化類型 : List<String>
- 實際類型參數(shù):
String>
- 泛型:
List<E>
- 形式類型參數(shù):
E
- 無限制通配符類型:
List<?>
- 原生態(tài)類型:
List
- 有限制類型參數(shù):
<E extends Number>
- 遞歸類型限制:
<T extends Comparable<T>
- 有限制通配符類型:
List <? extends Number>
- 泛型方法:
static <E> List<E> func(E[] e)
- 類型令牌:
String.class
第24條
@SuppressWarnings
注解使用
- 也可以使用在變量上
- 不能直接在
return
上使用该溯,這時創(chuàng)建局部變量接受返回值,然后注解
第25條 列表優(yōu)先于數(shù)組
-
第一個區(qū)別:數(shù)組是協(xié)變
(covariant)
别惦,泛型是不可變(invariant)
狈茉。//數(shù)組,編譯時合法 Object[] objects = new Long[1]; //Long 是Object的子類 objects[0] = "aa";//運行時會拋出異常 ArrayStroeException //集合掸掸,編譯時無法通過 List<Object> list = new ArrayList<Long>();//無法編譯通過氯庆,類型不匹配 list.add("aa");
第二個區(qū)別:數(shù)組是具體化
(reified)
運行時才知道并檢查類型蹭秋,泛型是通過擦除(erasure)
來實現(xiàn)的,編譯時就可以明確
創(chuàng)建泛型堤撵、參數(shù)化類型或者類型參數(shù)的數(shù)組是非法的仁讨。new List<E>[]
、new List<String>[]
实昨、new E[]
都是不合法的洞豁。
如果是合法的話,將創(chuàng)建出來“列表數(shù)組”保存到一個
Objcect
數(shù)組荒给,然后使用這個數(shù)組修改“列表數(shù)組”的類型丈挟,這時候再從原始的容器中取出內(nèi)容就會發(fā)生錯誤
技術(shù)角度來說,像E志电、List<E>
和List<String>
這樣的類型應該稱作不可具體化(nonreifiable)
曙咽,
不可具體化
(nonreifiable)
,指其運行時表示法包含的信息比它編譯時表示法包含的信息更少的類型唯一可具體化的參數(shù)化類型是無限制的通配符類型
第27條 優(yōu)先考慮泛型方法
將含有警告的raw type 方法修改為泛型方法
//raw type
public static Set union(Set s1, Set s2) {
Set result = new HashSet(s1);
result.addAll(s2);
return result;
}
//泛型方法
public static <E> Set<E> union(Set<E> s1, Set<E> s2) {
Set<E> result = new HashSet<E>(s1);
result.addAll(s2);
return result;
}
方法的局限性在于挑辆,三個集合的類型(兩個輸入?yún)?shù)和一個返回值)必須全部相同例朱。
利用有限制的通配符類型,讓這個方法變得更加靈活
泛型方法的一個顯著特性是鱼蝉,無需明確指定類型參數(shù)的值洒嗤,不像調(diào)用泛型構(gòu)造器的時候是必須指定的。編譯器通過檢查方法參數(shù)的類型來計算類型參數(shù)的值蚀乔。
通過某個包含該類型參數(shù)本身的表達式來限制類型參數(shù)是允許的烁竭。這就是遞歸類型限制 。遞歸類型限制最普遍的用途與Comparable
接口有關(guān)吉挣,他定義類型的自然順序:
public interface Comparable<T> {
int compareTo(T o);
}
類型參數(shù)T 定義的類型派撕,可以與實現(xiàn)Comparable<T>
的類型的元素進行比較。
下面是如何表達這種約束條件的一個示例
public static <T extends Comparable<T>> T max(List<T> list){...}
類型限制<T extends Comparable<T>>
睬魂,可以讀作"針對可以與自身進行比較的每個類型T"
//示例
public static <T extends Comparable<T>> T max(List<T> list) {
Iterator<T> iterator = list.iterator();
T result = iterator.next();
while (iterator.hasNext()) {
T t = iterator.next();
if (t.compareTo(result)>0) {
result = t;
}
}
return result;
}
第28條 利用有限制通配符來提升API的靈活性
<? extends E>
為在剛剛的Stack
類添加一個新方法pushAll(Iterable<E> iterable)
public void pushAll(Iterable<E> iterable) {
for (E e : iterable) {
push(e);
}
}
Stack<Number> numberStack = new Stack<>();
Iterable<Integer> integers = new ArrayList<>();
numberStack.pushAll(integers);//此時編譯不通過
將pushAll(Iterable<E> iterable)
修改為Iterable<? extends E> iterable
此時可以編譯通過
<? super E>
添加一個新方法
public void popAll(Collection<E> collection) {
while (!isEmpty())
collection.add(pop());
}
Stack<Number> numberStack1 = new Stack<>();
Collection<Object> objects = new ArrayList<>();
numberStack.popAll(objects);//此時編譯不通過
將popAll(Collection<E> collection)
修改為popAll(Collection<? super E> collection)
此時可以編譯通過
為了獲得最大限度的靈活性终吼,要在表示生產(chǎn)者或者消費者的輸入?yún)?shù)上使用通配符類型
助記符 PECS
PECS 表示producer-extends
consumer-super
如果表示生產(chǎn)者就使用<? extends T>
,如果表示消費者就使用<? super T>
如果使用得當氯哮,通配符類型對于類的用戶來說幾乎是無形的际跪。它們使方法能夠接受它們應該接受的參數(shù),并拒絕那些該拒絕的參數(shù)喉钢。如果類的用戶必須考慮通配符類型姆打,類的API或許就會出錯
//示例
<E> E reduce(List<E> list, Function<E> function, E initVal) {...}
//修改后
<E> E reduce2(List<? extends E> list, Function<E> function, E initVal) {...}
//示例2
public static <E> Set<E> union(Set<E> s1, Set<E> s2) {...}
//修改后 ,返回類型不要含有通配符
public static <E> Set<E> union(Set<? extends E> s1, Set<? extends E> s2) {... }
//兩者的區(qū)別在于
Set<Integer> integers = new HashSet<>();
Set<Double> doubles = new HashSet<>();
//使用通配符的方法可以通過一個【顯示的類型參數(shù)】來告訴要使用哪種類型
Set<Number> numbers = Math2.<Number>union(integers, doubles);
修改第27條中的max
方法
public static <T extends Comparable<T>> T max(List<T> list)
//修改后, 應用PECS轉(zhuǎn)換兩次
public static <T extends Comparable<? super T>> T max(List<? extends T> list)
//同時修改 Iterator<? extends T> iterator = list.iterator();
還有一個與通配符有關(guān)的話題值得探討肠虽。類型參數(shù)和通配符之間具有雙重性幔戏,許多方法都可以利用其中一個或者另一個進行聲明。
//示例
public static <E> void swap(List<E> list ,int i ,int j);
public static void swap(List<?> list ,int i ,int j);