什么是泛型?
Java 泛型(generics)是JDK5中引入的一種參數化類型特性。
為什么使用泛型,使用泛型的好處?
- 代碼更健壯(只要編譯期沒有警告,那么運行期就不會出現
ClassCastException
) - 代碼更簡潔(不用強轉)
- 代碼更靈活,復用
什么是參數化類型:
- 把類型當參數一樣傳遞
- <數據類型> 只能是引用類型(泛型的副作用)
舉個例子:
public interface Plate<T> {public void set(T t);public T get();
}
public class ATPlate<T> implements Plate<T> {private List<T> items = new ArrayList<T>(10);public <T> ATPlate<T> getATPlate() {return new ATPlate<T>();}
}
Plate<T>
中的 "T
” 稱為類型參數,Plate<T>
整個稱為泛型類型Plate<Banana>
中的 “Banana
” 稱為實際類型參數,Plate<Banana>
整個稱為參數化的類型ParameterizedType
泛型其實就是在類上面傳參,可以類比方法傳參,只不過類上面傳的參數類型也只能是類。
泛型類型可以通過extends
指定多個限定類型。
什么是多個限定類型,例如:
class A {}
interface B {}
interface C {}// 具有多個限定的類型變量是范圍中列出的所有類型的子類型。如果范圍之一是類,則必須首先聲明類
class D<T extends B & A & C> {} // 編譯報錯
class D<T extends A & B & C> {} // 編譯OK
泛型擦除
Q:Java 泛型的原理?什么是泛型擦除機制?
- Java 的泛型是 JDK5 引入的特性,為了向下兼容,虛擬機其實是不支持泛型,所以 Java 實現的是一種偽泛型機制,也就是說 Java 在編譯期擦除了所有的泛型信息,這樣 Java 就不需要產生新的類型到字節碼,所有的泛型類型最終都是一種原始類型,在 Java 運行時根本就不存在泛型信息。
Q:Java 編譯器具體是如何擦除泛型的?
- 檢查泛型類型,獲取目標類型
- 擦除類型變量,并替換為限定類型
- 如果泛型類型的類型變量沒有限定(
<T>
),則用Object
作為原始類型 - 如果有限定(
<T extends XCIass>
),則用XCIass
作為原始類型 - 如果有多個限定(
T extends XCIass1 & XCIass2
),則使用第一個邊界XCIass1
作為原始類型
- 如果泛型類型的類型變量沒有限定(
- 在必要時插入類型轉換以保持類型安全
- 生成橋方法以在擴展時保持多態性
例如,只有一個泛型 T
擦除后只有一個 Object
對象:
如果泛型有多個繼承限定類型,則使用第一個限定類作為擦除后的類型,此外如果泛型類繼承了支持泛型的接口,還會生成橋方法:
如何通過反射獲取泛型參數
泛型雖然被擦除了,但是在 類常量池 里面其實保留了泛型信息,所以可以通過反射獲取泛型的信息 getGenericType()
Java 的泛型擦除并不是將所有泛型信息全部都擦除了,會將類上和方法上聲明的泛型信息保存在字節碼中的 Signature
屬性中,這也是反射能夠獲取泛型的原因。但是在方法中的泛型信息是完全擦除了。
public class HelloWorld {Map<String, String> map;public static void main(String[] args){ try {Field field = HelloWorld.class.getDeclaredField("map"); Type type = field.getGenericType();System.out.println(type);// java.util.Map<java.lang.String, java.lang.String>System.out.println(type instanceof ParameterizedType); // trueParameterizedType pType = (ParameterizedType) type;System.out.print1n(pType.getRawType());// interface java.util.Map for (Type argType : pType.getActualTypeArguments()) {System.out.println(argType); // class java.lang.String }System.out.println(pType.getOwnerType()); // nullMethod method =HelloWorld.class.getMethod("applyMethod",Map.Entry.class);