目錄
泛型的向上轉型
将靜态方法的泛型類型和執行個體類型的泛型類型區分開
多泛型類
java可以建立泛型數組(待完善)
Java實作泛型的方法——擦拭法
由此,Java泛型的局限也展現出來
泛型繼承(loading)
通配符(loading)
泛型與反射(loading)
泛型的向上轉型
在Java标準庫中, ArrayList<T>實作了List<T>接口,它可以向上轉型為List<T>:
public class ArrayList<T> implements List<T> {
...
}
List<String> list = new ArrayList<String>();
編譯器看到泛型類型List<String>就可以自動推斷出後面的ArrayList<T>的泛型類型必須是ArrayList<String>,代碼可以簡寫為:
// 可以省略後面的Number,編譯器可以自動推斷泛型類型:
List<Number> list = new ArrayList<>();
!注意:不能把
ArrayList<Integer>
向上轉型為
ArrayList<Number>
或
List<Number>
。
👇
假設
ArrayList<Integer>
向上轉型為
ArrayList<Number>
:
// 建立ArrayList<Integer>類型:
ArrayList<Integer> integerList = new ArrayList<Integer>();
// 添加一個Integer:
integerList.add(new Integer(123));
// “向上轉型”為ArrayList<Number>:
ArrayList<Number> numberList = integerList;
// 添加一個Float,因為Float也是Number:
numberList.add(new Float(12.34));
// 從ArrayList<Integer>擷取索引為1的元素(即添加的Float):
Integer n = integerList.get(1); // ClassCastException!
- 一個ArrayList<Integer>轉型為ArrayList<Number>類型後,這個ArrayList<Number>就可以接受Float類型,因為Float是Number的子類。
- 而numberList 實際上和integerList 是同一個對象(ArrayList<Integer>類型),不可能接受Float類型, 在擷取Integer的時候将産生ClassCastException。
總結:
編譯器為了避免這種錯誤,根本就不允許把ArrayList<Integer>轉型為ArrayList<Number>。
!注意泛型的繼承關系:可以把ArrayList<Integer>向上轉型為List<Integer>(T不能變),但不能把ArrayList<Integer>向上轉型為ArrayList<Number>(T不能變成父類)
▲另外須知: ArrayList<Integer>和ArrayList<Number>兩者完全沒有繼承關系。
将靜态方法的泛型類型和執行個體類型的泛型類型區分開
注意,泛型類型不能用于靜态方法
下面的代碼編譯錯誤
public class Pair<T> {
private T first;
private T last;
public Pair(T first, T last) {
this.first = first;
this.last = last;
}
public T getFirst() { ... }
public T getLast() { ... }
// 對靜态方法使用<T>:
public static Pair<T> create(T first, T last) {
return new Pair<T>(first, last);
}
}
👇正确表示如下
public class Pair<T> {
private T first;
private T last;
public Pair(T first, T last) {
this.first = first;
this.last = last;
}
public T getFirst() { ... }
public T getLast() { ... }
// 靜态泛型方法應該使用其他類型區分:
public static <K> Pair<K> create(K first, K last) {
return new Pair<K>(first, last);
}
}
多泛型類
class Pair<T,K>
{
private T first;
private K second;
public Pair(T first, K second){
this.first = first;
this.second = second;
}
public T getFirst(){
return this.first;
}
public K getSecond(){
return this.second;
}
}
public class pairx {
public static void main(String[] args) {
Pair<String, Integer> p1 = new Pair<>("泥煙", 8080);
System.out.println(p1.getFirst()+p1.getSecond());
}
}
運作結果:
輸出→ 泥煙8080
另外java是可以建立泛型數組的(還未完全掌握,之後完善):
java可以建立泛型數組(待完善)
Java實作泛型的方法——擦拭法
Java的泛型是由編譯器在編譯時實行的,編譯器内部永遠把所有類型
T
視為
Object
處理,但是,在需要轉型的時候,編譯器會根據
T
的類型自動為我們實行安全地強制轉型。
由此,Java泛型的局限也展現出來
局限一:不能是基本類型,例如
<T>
,因為實際類型是
int
,
Object
類型無法持有基本類型:
Object
局限二:無法取得帶泛型的Pair<int> p = new Pair<>(1, 2); // compile error!
Class
有如下類
class Pair<T> { private T first; private T last; public Pair(T first, T last) { this.first = first; this.last = last; } public T getFirst() { return first; } public T getLast() { return last; } }
無論Pair<String> p1 = new Pair<>("Hello", "world"); Pair<Integer> p2 = new Pair<>(123, 456); Class c1 = p1.getClass(); Class c2 = p2.getClass(); System.out.println(c1==c2); // true System.out.println(c1==Pair.class); // true
的類型是什麼,
T
傳回同一個
getClass()
執行個體,因為編譯後它們全部都是
Class
Pair<Object>
局限三:無法判斷帶泛型的類型,原因同上
Pair<Integer> p = new Pair<>(123, 456); // Compile error: if (p instanceof Pair<String>) { ... }
并不存在Pair<String>.class,而是隻有唯一的Pair.class
局限四:不能直接執行個體化
類型
T
将其執行個體化的方法public class Pair<T> { private T first; private T last; public Pair() { // Compile error: first = new T(); last = new T(); } } 擦拭後實際上變成了: first = new Object(); last = new Object();