由于本人能力有限毫蚓,文中若有錯(cuò)誤之處扇丛,歡迎指正绿映。
轉(zhuǎn)載請(qǐng)注明出處:http://www.reibang.com/p/75bc58480c11
什么是泛型?
泛型扛芽,即
類型參數(shù)化
骂蓖。
- 泛型是 JDK5 中引入的一種安全機(jī)制。泛型的引入把運(yùn)行時(shí)期易產(chǎn)生的
ClassCastException
轉(zhuǎn)化到編譯時(shí)期川尖。(下面有示例)- Java中的泛型是偽泛型登下,在JVM層并不真正支持泛型。在編譯檢查成功后叮喳,相應(yīng)的class文件中已經(jīng)沒有了泛型的信息被芳。這種機(jī)制叫做 擦除補(bǔ)償 機(jī)制。(下面有示例)
- 泛型使用最廣泛的地方是Java集合框架馍悟。
- 可以利用泛型的特點(diǎn)筐钟,設(shè)計(jì)出更加靈活的API。
// JDK5之前沒有泛型
List strs1 = new ArrayList();
strs1.add("hello world!");
// 可以添加赋朦,但在使用是可能是產(chǎn)生java.lang.ClassCastException
// strs1.add(123);
String str1 = (String) strs1.get(0);
// JDK6有泛型
List<String> strs2 = new ArrayList<String>();
strs2.add("hello world!");
// strs2.add(123); // 編譯時(shí)期直接報(bào)錯(cuò)
String str2 = strs2.get(0); // 不用強(qiáng)制類型轉(zhuǎn)換
// 通過反射證明偽泛型
List<String> list = new ArrayList<>();
list.add("hello world!");
Method add = list.getClass().getMethod("add", Object.class);
add.invoke(list, 111);
System.out.println(list.get(1)); // java.lang.ClassCastException
泛型類
public class Container<V> {
private V value;
public Container(V v) {
value = v;
}
public V getValue() {
return value;
}
public void setValue(V value) {
this.value = value;
}
}
泛型接口
public interface Generator<T> {
public T next();
}
泛型方法
使用原則: 無論何時(shí),只要你能做到李破,你就應(yīng)該盡量使用泛型方法宠哄。也就是說,如果使用泛型方法可以取代將整個(gè)類泛化嗤攻,那么應(yīng)該優(yōu)先采用泛型方法毛嫉。
public class Main {
public static <T> void outPrint(T t) {
System.out.println(t);
}
public static void main(String[] args) {
outPrint("findingsea");
outPrint(123);
outPrint(true);
}
}
通配符
使用原則(PECS)
1.如果要從集合中讀取類型T的數(shù)據(jù),并且不能寫入妇菱,可以使用 ? extends 通配符承粤;(Producer Extends)
2.如果要從集合中寫入類型T的數(shù)據(jù)暴区,并且不需要讀取,可以使用 ? super 通配符辛臊;(Consumer Super)
3.如果既要存又要取仙粱,那么就不要使用任何通配符。
- ?
無邊界通配符彻舰,它的使用形式是一個(gè)單獨(dú)的問號(hào):List<?>伐割,也就是沒有任何限定,不做任何限制刃唤。
- 上限(<? extends T>)
List<? extends Fruit> flist = new ArrayList<Apple>();
// 編譯錯(cuò)誤隔心,不能添加任何類型
// flist.add(new Apple());
// flist.add(new Fruit());
// flist.add(new Object());
flist.add(null); // null不處于任何類型
// 可以獲取到具體類型
Fruit f = flist.get(0);
flist 的類型是 List<? extends Fruit>,我們可以把它讀作:一個(gè)類型的 List尚胞, 這個(gè)類型可以是繼承了 Fruit 的某種類型硬霍。注意,這并不是說這個(gè) List 可以持有 Fruit 的任意類型笼裳。而是我們不知道這個(gè) List 到底持有什么類型唯卖,所以不能安全的添加一個(gè)對(duì)象。 另一方面侍咱,如果調(diào)用某個(gè)返回 Fruit 的方法耐床,這是安全的。因?yàn)槲覀冎佬ǜ谶@個(gè) List 中撩轰,不管它實(shí)際的類型到底是什么,但肯定能轉(zhuǎn)型為 Fruit昧廷,所以編譯器允許返回 Fruit堪嫂。
- 下限(<? super T>)
List<? super Apple> apples = new ArrayList<>();
apples.add(new Apple());
apples.add(new RedApple());
// apples.add(new Fruit()); // 編譯錯(cuò)誤
apples 的類型是 List<? super Apple>,它表示某種類型的 List木柬,這個(gè)類型是 Apple 的基類型皆串。也就是說,我們不知道實(shí)際類型是什么眉枕,但是這個(gè)類型肯定是 Apple 的父類型恶复。因此,我們可以知道向這個(gè) List 添加一個(gè) Apple 或者其子類型的對(duì)象是安全的速挑,這些對(duì)象都可以向上轉(zhuǎn)型為 Apple谤牡。但是我們不知道加入 Fruit 對(duì)象是否安全,因?yàn)槟菢訒?huì)使得這個(gè) List 添加跟 Apple 無關(guān)的類型姥宝。
寫在最后
- 開發(fā)中翅萤,泛型使用最多的地方就是集合框架。大部分情況下泛型的使用還是比較簡(jiǎn)單的腊满。
- 另外套么,泛型的使用多見于一些開源框架中培己。泛型的引入大大增強(qiáng)了API設(shè)計(jì)的靈活性。
- 如果你不確定一個(gè)地方能不能使用泛型胚泌,那么請(qǐng)嘗試使用它省咨。