1 什么是泛型
泛型即參數化類型,即是將類型由原本的類型參數化,類似于方法中的變量參數,此時類型也可以當作參數傳入(可以稱之為類型參數)。
2 為什么要使用泛型
先看一段代碼:
List list = new ArrayList();
list.add("Acey");
list.add(100);
for (int i = 0; i < list.size(); i++) {
String name = (String) list.get(i); //取出Integer時,運行時出現異常
System.out.println("name:" + name);
}
當我們在list
中插入一個字符串和整數時是合法的,因為list
默認的泛型即為Object
類型。但是當我們從集合中取出數據時,需要對不同的類型進行強轉成符合的對應的類型。為了解決這一問題,泛型就出現了。
3 泛型的使用
- Java泛型變成時在jdk1.5版本后引出的,使用的時候注意了。。
- 泛型只在編譯階段有用,在編譯的過程中,將會正確的檢驗泛型的結果,將泛型的相關信息擦除,在對象進入和離開方法的邊界時會加上對應的類型轉換方法。也就是說在成功編譯成class文件后將不會在包含任何泛型的信息。
4 泛型方法
對于常見的泛型模式,推薦的名稱:
K - 鍵
V - 值
E - 異常
T - 泛型
泛型方法使得該方法能獨立于類而產生變化。以下是一個基本的指導原則:無論何時,只要你能做到,你就應該盡量使用泛型方法。也就是說,如果使用泛型方法可以取代將整個類泛型化,那么就應該只使用泛型方法,因為它可以使事情更清楚明白。另外,對于一個static的方法而言,無法訪問泛型類的類型參數。所以,如果static方法需要使用泛型能力,就必須使其成為泛型方法。
要定義泛型方法,只需將泛型參數列表置于返回值之前,就像下面這樣:
public <T> void f(T x){
System.out.println(x.getClass().getName());
}
可變參數與泛型方法
public static <T> List<T> makeList(T... args){
List<T> result = new ArrayList<T>();
for(T item:args)
result.add(item);
return result;
}
5 構建一個復雜的元組
元組和``list```列表一樣,都是用來存儲數據,但是和列表不同的是,列表只能存儲相同的數據類型,而元組可以存儲多種數據類型,且可以根據自己的需求無線擴展。
class ThreeTuple2<A,B,C>{
public final A first;
public final B second;
private final C three;
public ThreeTuple2(A a,B b,C c){
first = a;
second = b;
three = c;
}
public String toString(){
return "(" + first + "," + second + "," + three + ")";
}
}
6 泛型通配符 “ ? ”
在不確定使用哪種類型的時候可以使用泛型通配符 “? ”來代替,但是在操作該類型的對象時,只能使用Object
中的功能。
Class<?>classType = Class.forName("java.lang.String");
7 泛型的上限和下限
上限:? extends E 可以接收 E 類型或E類型以下的類型對象
上限:? super E 可以接收E類型或E類型以上的類型對象。
7 泛型擦除
java 泛型是用泛型擦除來實現的,類型擦除就是說Java泛型只能用于在編譯期間的靜態類型檢查,然后編譯器生成的代碼會擦除相應的類型信息,這樣到了運行期間實際上JVM根本就知道泛型所代表的具體類型。這樣做的目的是因為Java泛型是1.5之后才被引入的,為了保持向下的兼容性,所以只能做類型擦除來兼容以前的非泛型代碼
public class Node<T> {
private T data;
private Node<T> next;
public Node(T data, Node<T> next) {
this.data = data;
this.next = next;
}
public T getData() { return data; }
}
編譯器在做了相應的檢查后,在運行期間上面的代碼會轉換為
public class Node {
private Object data;
private Node next;
public Node(Object data, Node next) {
this.data = data;
this.next = next;
}
public Object getData() { return data; }
// ...
}
這也就是說,我們若只是給定了泛型參數 T 的話,那么不管我們傳進來的是 Integer 還是 String .... 最終都會被當作 Object 來看待。
如果我們想要設置自己的 bounds ,那么我們就應該使用上限來設置。
public class Node<T extends Comparable<T>> {
private T data;
private Node<T> next;
public Node(T data, Node<T> next) {
this.data = data;
this.next = next;
}
public T getData() { return data; }
// ...
}