
1. 泛型的概念
1.1 什么是泛型
泛型類似標(biāo)簽,出現(xiàn)原因是因?yàn)椋杭先萜黝愒谠O(shè)計(jì)階段/聲明階段不能確定這個(gè)容器到底實(shí)際存的是什么類型的對(duì)象
因此此時(shí)把元素的類型設(shè)計(jì)成一個(gè)參數(shù),這個(gè)類型參數(shù)叫做泛型
泛型,允許在定義類、接口時(shí)通過一個(gè)標(biāo)識(shí)表示類中某個(gè)屬性的類型或者是某個(gè)方法的返回值及參數(shù)類型
Collection<E>,List<E>,ArrayList<E> 這個(gè)<E>就是類型參數(shù),即泛型
1.2 泛型的好處
- 解決元素存儲(chǔ)的安全性問題,好比商品、藥品標(biāo)簽,不會(huì)弄錯(cuò)。
- 解決獲取數(shù)據(jù)元素時(shí),需要類型強(qiáng)制轉(zhuǎn)換的問題,好比不用每回拿商品、藥品都要辨別。


2. 集合中使用泛型
2.1 使用方法
- 集合接口或集合類在jdk5.e時(shí)都修改為帶泛型的結(jié)構(gòu)。
- 在實(shí)例化集合類時(shí),可以指明具體的泛型類型。
- 指明完以后,在集合類或接口中凡是定義類或接口時(shí),內(nèi)部結(jié)構(gòu)使用到類的泛型的位置,都指定為實(shí)例化的泛型類型。
- 注意點(diǎn):泛型的類型必須是類,不能是基本數(shù)據(jù)類型。需要用到基本數(shù)據(jù)類型的位置,拿包裝類替換。
- 如果實(shí)例化時(shí),沒有指明泛型的類型。默認(rèn)類型為java.Lang.Object類型。
2.2 具體舉例
以ArrayList為例
//導(dǎo)入的包有:import org.junit.Test;import java.util.*;
public class GenericTest {
@Test
public void test1(){
ArrayList<Integer> list = new ArrayList<>();
list.add(23);
list.add(97);
list.add(25);
list.add(43);
//編譯時(shí),就會(huì)進(jìn)行類型檢查,保證數(shù)據(jù)安全
// list.add("Tom");
//遍歷方式一:for
for(Integer score : list){
//避免了強(qiáng)轉(zhuǎn)操作
int stuScore = score;
System.out.println(stuScore);
}
//遍歷方式二:Iterator
Iterator<Integer> iterator = list.iterator();
while(iterator.hasNext()){
int stuScore = iterator.next();
System.out.println(stuScore);
}
}
}
以HashMap為例
//導(dǎo)入的包有:import org.junit.Test;import java.util.*;
public class GenericTest {
@Test
public void test2(){
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("Tom",83);
map.put("Jerry",77);
map.put("Jack",33);
//泛型的嵌套
Set<Map.Entry<String, Integer>> entry = map.entrySet();
Iterator<Map.Entry<String, Integer>> iterator = entry.iterator();
//遍歷
while(iterator.hasNext()){
Map.Entry<String, Integer> e = iterator.next();
String key = e.getKey();
Integer value = e.getValue();
System.out.println(key + ":" + value);
}
}
}
3. 自定義泛型結(jié)構(gòu)
3.1 泛型類與泛型接口
如何定義
泛型類與泛型接口的區(qū)別主要還是類與接口的區(qū)別,所以這里以泛型類舉例
public class Order<T> {
String orderName;
int orderId;
//類的內(nèi)部結(jié)構(gòu)就可以使用泛型
T orderT;
public Order(){
}
public Order(String orderName,int orderId,T orderT){
this.orderName = orderName;
this.orderId = orderId;
this.orderT = orderT;
}
}
class Test{
@org.junit.Test
public void test1(){
//要求:如果定義了類是帶泛型的,建議在實(shí)例化時(shí)使用它
Order<String> order = new Order<String>("AA",1001,"hello");
}
}
注意事項(xiàng)
- 泛型類可能有多個(gè)參數(shù),此時(shí)應(yīng)將多個(gè)參數(shù)一起放在尖括號(hào)內(nèi)。比如:<E1,E2,E3>
- 泛型類的構(gòu)造器如下:public GenericClass(){},而下面是錯(cuò)誤的:public GenericClass<E>(){}
- 實(shí)例化后,操作原來泛型位置的結(jié)構(gòu)必須與指定的泛型類型一致。
- 泛型不同的引用不能相互賦值。
- 泛型如果不指定,將被擦除,泛型對(duì)應(yīng)的類型均按照Object處理,但不等價(jià)于Object。
- 如果泛型結(jié)構(gòu)是一個(gè)接口或抽象類,則不可創(chuàng)建泛型類的對(duì)象。
- jdk1.7,泛型的簡(jiǎn)化操作:ArrayList<Fruit> flist = new ArrayList<>()。
- 泛型的指定中不能使用基本數(shù)據(jù)類型,可以使用包裝類替換。
- 在靜態(tài)方法中不能使用類的泛型。
- 異常類不能是泛型的。
- 不能使用new E[]。但是可以:E[] elements = (E[])new Object[capacity]。
- 父類有泛型,子類可以選擇保留泛型也可以選擇指定泛型類型:
- 子類不保留父類的泛型:按需實(shí)現(xiàn)
- 沒有類型,擦除
- 具體類型
- 子類保留父類的泛型:泛型子類
- 全部保留
- 部分保留
- 子類不保留父類的泛型:按需實(shí)現(xiàn)
3.2 泛型方法
在方法中出現(xiàn)了泛型的結(jié)構(gòu),泛型參數(shù)與類的泛型參數(shù)沒有任何關(guān)系
也就是說,泛型方法所屬的類是不是泛型類都沒有關(guān)系
泛型參數(shù)在調(diào)用時(shí)體現(xiàn)泛型參數(shù)的類型
泛型方法可以是靜態(tài)的,是因?yàn)榉盒蛥?shù)是在調(diào)用方法時(shí)確定的
public <E> List<E> testMethod(E[] arr){
ArrayList<E> list = new ArrayList<>();
for(E e : arr){
list.add(e);
}
return list;
}
4. 泛型在繼承上的體現(xiàn)
若子類在繼承帶泛型的父類時(shí),指明了泛型類型,則實(shí)例化子類對(duì)象時(shí),不再需要指明泛型。
若子類在繼承帶泛型的父類時(shí),沒有指明了泛型類型,則實(shí)例化子類對(duì)象時(shí),需要指明泛型。
若類A是類B的父類,但G<A>和G<B>二者不具備子父類關(guān)系,二者是并列關(guān)系
若類A是類B的父類,A<G>是B<G>的父類
5. 通配符的使用
5.1 通配符的概念
通配符的符號(hào)是"?",它的使用方法為使用"?"代替具體的類型參數(shù)
例如:類A是類B的父類,G<A>和G<B>是沒有關(guān)系的,但二者共同的父類是G<?>
//導(dǎo)入的包:import org.junit.Test;import java.util.List;
public class TongPeiFu {
@Test
public void test1(){
List<Object> list1 = null;
List<Object> list2 = null;
List<?> list = null;
list = list1;
list = list2;
}
}
5.2 通配符的寫入與讀取
對(duì)于List<?>不能向其內(nèi)部添加數(shù)據(jù),因?yàn)椴恢榔渥宇愄砑拥氖鞘裁磾?shù)據(jù)
對(duì)于List<?>允許讀取數(shù)據(jù),讀取數(shù)據(jù)類型為Object
//導(dǎo)入的包有:import org.junit.Test;import java.util.ArrayList;import java.util.List;
public class TongPeiFu {
@Test
public void test2(){
List<String> list3 = new ArrayList<>();
list3.add("AA");
list3.add("BB");
list3.add("CC");
List<?> list = list3;
//添加:對(duì)于List<?>不能向其內(nèi)部添加數(shù)據(jù)
//除了添加null之外
list.add(null);
//獲?。涸试S讀取數(shù)據(jù),讀取數(shù)據(jù)類型為Object
Object o = list.get(0);
System.out.println(o);//AA
}
}
5.3 有限制的通配符
- G<? extends A>:可以作為G<A>和G<B>的父類,其中B是A的子類
- G<? super A>:可以作為G<A>和G<B>的父類,其中B是A的父類
//導(dǎo)入的包有:import org.junit.Test;import java.util.ArrayList;import java.util.List;
public class TongPeiFu {
@Test
public void test2(){
List<? extends Person> list1 = null;
List<? super person> list2 = null;
List<Student> list3 = null;
List<Person> list4 = null;
List<Object> list5 = null;
//G<? extends A>可以作為包括A以下類的父類
list1 = list3;
list1 = list4;
// list1 = list5;
//G<? super A>可以作為包括A以上類的父類
// list2 = list3;
list2 = list4;
list2 = list5;
}
}