Java(7)泛型
一、泛型概述
1、什麼是泛型
-
泛型就是標籤,加了泛型,就相當於加了標籤,這個容器就只能放這一類物品。
-
把元素的類型設參數,這個類型參數叫做泛型。
Collection<E>,List<E>,ArrayList<E>
這個<E>
就是類型參數,即泛型。 -
所謂泛型,就是允許在定義類、介面時通過一個標識表示類中某個屬性的類型或者是某個方法的返回值及參數類型。這個類型參數將在使用時(例如, 繼承或實現這個介面,用這個類型聲明變數 、創建對象時)確定(即傳入實 際的類型參數,也稱為類型實參)。
-
JDK1.5時引入。
2、為什麼用泛型
- 存儲時:編譯時進行類型檢查來保證,存放的都是類型一樣的數據,更安全。
- 獲取時:獲取的都是類型一樣的數據,無序強轉。
3、在集合中使用泛型
- ① 集合介面或集合類在jdk5.0時都修改為帶泛型的結構。
-
② 在實例化集合類時,可以指明具體的泛型類型。
-
③ 指明完以後,在集合類或介面中凡是定義類或介面時,內部結構(比如:方法、構造器、屬性等)使用到類的泛型的位置,都指定為實例化的泛型類型。
比如:add(E e) —>實例化以後:add(Integer e)。
-
④ 注意點:泛型的類型必須是類,不能是基本數據類型。需要用到基本數據類型的位置,拿包裝類替換。
-
⑤ 如果實例化時,沒有指明泛型的類型。默認類型為java.lang.Object類型。
-
⑥在JDK7中新特性:類型推斷
List<String> list = new List<>();
後面的泛型可以省略
二、自定義泛型結構
1、泛型類、介面
- 自定義泛型類程式碼實現:核心思想就是把T當做一個某某類型的參數,只不過需要在尖括弧標明
/**
* 自定義泛型類
*/
public class MyGeneric<T> {
String name;
int age;
T decs;//可以把他當做一個類來看,但是並不是類,而是參數
public MyGeneric() {
}
//帶參構造器
public MyGeneric(String name, int age, T decs) {
this.name = name;
this.age = age;
this.decs = decs;
}
//get()方法
public T getDecs() {
return decs;
}
//set()方法
public void setDecs(T decs) {
this.decs = decs;
}
//toString()方法
@Override
public String toString() {
return "MyGeneric{" +
"name='" + name + '\'' +
", age=" + age +
", decs=" + decs +
'}';
}
}
- 子類繼承父類時,泛型的情況
- 說明:子類是「富二代」,除了指定或保留父類的泛型,還可以增加自己的泛型。
- 情況一:子類不保留父類的泛型
- 父類沒有類型,擦除,類型按照Object處理
- 父類是具體類型
- 情況二:子類保留父類的類型
- 全部保留
- 部分保留
- 程式碼示例
class Father<T1, T2>{
}
//子類不保留父類的泛型
//1)沒有類型,擦除--->這時,子類不是泛型
class Son1 extends Father{//等價於class Son extends Father<Object,Object>{
}
//2)具體類型--->這時,子類不是泛型
class son2 extends Father<Integer, String>{
}
//子類保留父類的泛型
//1)全部保留
class Son3<T1, T2> extends Father<T1, T2>{
}
//2)部分保留
class Son4<T2> extends Father<Integer, T2>{
}
//當然子類也可以加上自己的新的泛型類型
-
注意點
-
泛型類里的泛型是以參數的形式存在的。
-
可以有多個參數:
<T1,T2,T3>
-
聲明構造器時
- 空參構造器和普通的一樣
public MyGeneric() {}
- 帶參構造器體現在參數上
public MyGeneric(String name, int age, T decs) {
- 空參構造器和普通的一樣
-
泛型不同的引用不能相互賦值
ArrayList<String> list1 = new ArrayList<>(); ArrayList<Integer> list2 = new ArrayList<>(); list1 = list2;//這樣是錯誤的!
-
泛型如果不指定,就會被擦除,泛型對應的類型按照Object處理,但不等價於Object。
-
在類/介面上聲明的泛型,在本類或本介面中即代表某種類型,可以作為非靜態 屬性的類型、非靜態方法的參數類型、非靜態方法的返回值類型。但在靜態方法中不能使用類的泛型。(因為,靜態方法隨著類的載入而載入,而泛型是在實例化的時候指定,晚於靜態方法出生)
-
異常類不能聲明為泛型的。
-
如果想在構造器中初始化一個T類型的數組,要寫作:
T[] t = (T[])new Object[capacity]
-
2、泛型方法
- 理解
- 在方法中出現了泛型的結構,就是泛型方法。
- 泛型方法中的泛型參數與類的泛型參數沒有關係。也就是說:泛型方法所屬的類是不是泛型無所謂。
- 可以聲明為靜態的,因為泛型參數是在調用方法時確定的,而不是來載入時。
- 程式碼實現一個泛型方法
public static <E> List<E> copyFromArrayToList(E[] arr){
//這裡的第一個<E>是為了不讓編譯器把E當做是一個我們自定義的類
ArrayList<E> list = new ArrayList<>();
for(E e : arr){
list.add(e);
}
return list;
}
三、舉例泛型類和泛型方法的使用場景
1、泛型類舉例:
連接資料庫時,操作資料庫中的表的記錄
-
DAO類:定義了資料庫不同表的一些共性操作的類
public class DAO<T> {}
-
CustomerDAO:定義了專門操作Customer表的類
public class CustomerDAO extends DAO<Customer>{}
2、泛型方法舉例
//泛型方法舉例
/*
返回的東西是不確定的,比如
需求1:表中有多少條記錄
需求2:獲取工資最大值
*/
public <E> E getSomething(){
return null;
}
四、泛型在繼承上的體現
- 首先,我們知道在多態下,子類的對象可以賦給父類的引用。
- 但是,在泛型情況下,不再存在子父類的關係。
例如,String是Object的子類,但是List<String >並不是List<Object> 的子類。
同理在方法參數中也一樣適用。這樣就導致一個問題,同一功能的方法要重載很多次。 - 區別,類B是類A的子類,那麼
B<T>仍然是A<T>
的子類。
五、通配符
1、通配符
通配符用 ?
來表示,含義為:類A是類B的父類,G<A> G<B>
並不存在什麼關係,二者的共同父類是:G<?>
。這樣就不必重載那麼多功能相同的方法了。
2、添加通配符後數據的寫入何人讀出
- 寫入:對於List<?>,不能向其中添加數據,只能添加null;
- 讀出:可以讀取,讀取出的數據為Object類型
3、有限制條件的通配符
- 首先,通配符
?
可以的範圍可以理解為: (無窮小,無窮大) ? extends A
的範圍就可以理解為:小於等於,即(無窮小,A]? super A
的範圍就可以理解為:大於等於,即[A,無窮大)