泛型程式設計:意味著編寫的程式碼可以被很多不同型別的物件所重用。
型別引數:ArrayList<String> files = new ArrayList<>()
(String便是型別引數,且構造器中可省去,省去的型別依據變數的型別推斷)
泛型類: 具有一個或多個型別變數的類。
public class Pair<T>
{
private T first;
private T second;
public Pair() {first = null;second = null ;}
public Pair(T first,T second) {this.first = first;this.second = second;}
public T getFirst() {return first;}
public T getSecond() {return second ;}
public void setFirst(T newValue) {first = newValue;}
public void setSecond(T newValue) {second = newValue;}
}
//可以有多個型別變數 :public calss Pair<T,U>{...}(通常事大寫的形式)
複製程式碼
泛型方法: 型別變數放在修飾符的後面,返回型別的前面(該方法既可以放在普通類,也可以放在泛型類)
class ArrayAlg
{
public static <T> T getMiddle(T... a)
{
return a[a.length/2];
}
}
複製程式碼
泛型方法的呼叫:
String middle = ArrayAlg.<String>getMiddle("John","A","Public");
- 大多數情況下可省去
<String>
型別引數(編譯器可推斷);
型別變數限定:保證使用該型別物件的時候呼叫的方法存在
public static <T extends Comparable> T min(T[] a)
{
if(a == null || a.length == 0) return null;
T smallest = a[0];
for(int i = 0 ;i < a.length;i++)
if(smallest.compareTo(a[i]) > 0) smallest = a[i];
return smallest;
}
複製程式碼
- 在型別變數中的
extends
表示的是T是繫結型別的子型別,T可以繫結的型別既可以是類,也可以是介面。 - 繫結多個限定:
T extends Comparable & Serializable
(限定型別用‘&’分隔,‘,’分隔型別變數)
泛型程式碼和虛擬機器:
- 型別擦除:定義泛型型別的同時提供了相應的原始型別(原始型別的名字便是刪去型別引數後的泛型型別名),擦除型別變數,並替換為限定型別(無限定型別的用Object),結果是一個普通的類。
public class Pair
{
private Object first;
private Object second;
public Pair() {first = null;second = null ;}
public Pair(Object first,Object second) {this.first = first;this.second = second;}
public Object getFirst() {return first;}
public Object getSecond() {return second ;}
public void setFirst(Object newValue) {first = newValue;}
public void setSecond(Object newValue) {second = newValue;}
}
複製程式碼
- 有限定型別的情況下,選第一個作為原始型別的替換
public class Interval<T extends Comparable & Serializable > implements Serializable
{
private T lower;
private T upper;
...
public Interval(T first,Ts econd)
{
if(first.compareTo(second)<=0){lower=first;upper=second;}
else{lower=second;upper=first;}
}
}
//原始型別
public class Interval implements Serializable
{
private Comparable lower;
private Coiparable upper;
....
public Interval(Coiparable first,Coiparable second){...}
}
複製程式碼
為了提高效率,最好將標籤介面(沒有方法的介面)放在邊界末尾 翻譯泛型表示式:呼叫泛型方法,若擦除返回型別,編譯器插入強制型別轉換 橋方法:為了解決多型與型別擦除的衝突 Java泛型轉換的事實:
- 虛擬機器中沒有泛型,只有普通的類和方法
- 所有的型別引數都用它們的限定型別替換。
- 橋方法被合成來保持多型
- 為保持型別安全性,必要時插入強制型別轉換
約束與侷限性:
- 不能用型別引數代替基本型別;類似的 沒有
Pair<double>
,只有Pair<Double>
- 執行時型別查詢只適用於原始型別,(虛擬機器中總有一個特定的非泛型型別,So所有的型別查詢只產生原始型別)
- 不能建立引數化型別的陣列(記住,是不能建立
Pair<String>[] T = new Pair<String>[10];
,宣告變數Pair<String>[] T
還是可以的, 原因:對於泛型型別,即使通過了陣列儲存檢查,還是導致型別錯誤。 - Varargs警告(向引數個數可變的方法傳遞一個泛型型別的例項,也就是說引數可變時,實際建立的時一個引數陣列,這時建立的時泛型型別的陣列,在這種情況下,不會報錯,而是含警告)
- 不能例項化型別變數,例如
publicPair(){first=newT();second=newT();}//Error
(型別擦除將T改變成Object,解決方法: javaSE之後可以讓呼叫者提供構造器表示式,傳統的辦法是使用Class.newInstance
構造泛型物件) - 不能構造泛型陣列
- 泛型類的靜態上下文中型別變數無效(不能在靜態域或方法中引用型別變數)
- 不能丟擲或捕獲泛型類的例項(在異常規範中使用變數是允許的)
- 可以消除對受查異常的檢查(異常處理回憶:必須為所有受查異常提供一個處理器)
- 注意擦除後的衝突(要想支援擦除的轉換,就需要強行限制一個類或型別變數不能同時成為兩個介面型別的子類,而這兩個是同一個介面的不同引數化)
泛型型別的繼承規則
- 舉個例子,
Pair<T>
與Pair<S>
是沒有關係的 - 永遠可以將引數化型別轉換為一個原始型別
- 泛型類可以擴充套件或實現其他的泛型類
萬用字元型別:
- 萬用字元型別中,允許型別引數變化。(上邊界限定萬用字元,安全的讀泛型物件讀取(如接受返回值))萬用字元型別:
Pair<?extends Employee〉
-
- (下邊界限定萬用字元,向泛型物件寫入(如傳遞引數)萬用字元的超型別限定:
? super Manager
,這個萬用字元限制為Manager的所有的超型別 - 它的行為:.可以為方法提供引數,但不能使用返回值
? super Manager getFirst() void setFirst(? super Manager)
(先看第一個返回的,由於返回的型別是Manager的超類,所以Object類呀Employee呀都有可能,那麼你用什麼型別的引用去接收這個返回值?編譯器無法知道getFirst返回值的真正型別,只有通過Object引用去接收才是合法的。再看第二個setFirst引數中的萬用字元,編譯器同樣無法知道引數的具體型別,但是這個型別是有“下界”的,就是說傳來的引數只要是Manager類或者它的子類,那麼這樣的傳參都是可以接受的,編譯器由於知道下界,這些傳參就是安全的。)- 無限定萬用字元:
Pair<?>
- (下邊界限定萬用字元,向泛型物件寫入(如傳遞引數)萬用字元的超型別限定: