泛型
泛型是JDK1.5之后的一項新增特性, 它的本質是參數化類型(Parametersized Type)的應用,
即所操作的數據類型被指定為一個參數,這種參數類型可以用在類、接口和方法的創(chuàng)建中,分別為泛型類、泛型接口和泛型方法。
Java中的泛型只在程序源代碼中存在, 在編譯后的字節(jié)碼文件中就已經替換為原來的原生類型(Raw Type), 并在相應的地方插入強制類型裝換代碼。
對于運行期的Java語言來說,ArrayList<int> 與 ArrayList<String> 就是同一個類,泛型技術實際上就是Java語言的一顆語法糖,Java語言中的泛型實現方法稱為類型擦除,基于這種方法的實現的泛型稱為偽泛型。
java 字節(jié)碼層面
源代碼
public class MyListTest {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("test");
String list1 = list.get(0);
System.out.println(list1);
System.out.println(list);
}
}
編譯命令
javac -encoding UTF-8 MyListTest.java
編譯為class文件
public class MyListTest {
public MyListTest() {
}
public static void main(String[] var0) {
ArrayList var1 = new ArrayList();
var1.add("test");
String var2 = (String)var1.get(0); // 這里插入強制類型轉換
System.out.println(var2);
System.out.println(var1);
}
}
javap查看字節(jié)碼
javap -c MyListTest
警告: 二進制文件MyListTest包含com.james.example.MyListTest
Compiled from "MyListTest.java"
public class com.james.example.MyListTest {
public com.james.example.MyListTest();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: new #2 // class java/util/ArrayList
3: dup
4: invokespecial #3 // Method java/util/ArrayList."<init>":()V
7: astore_1
8: aload_1
9: ldc #4 // String test
11: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
16: pop
17: aload_1
18: iconst_0
19: invokeinterface #6, 2 // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object; 這里獲取的是Object類型
24: checkcast #7 // class java/lang/String 類型轉換指令
27: astore_2
28: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;
31: aload_2
32: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
35: getstatic #8 /
Java中獲取泛型的類型信息
Java泛型的引入,各種場景(虛擬機解析, 反射等)下的方法調用都可能對原有的基礎產生影響和新需求, 如在泛型類中如何獲取傳入的參數化類型等?;诖耍琂CP組織對虛擬機規(guī)范做出了相應的修改,引入了諸如Signature, LocalVariableType Table 等新屬性解決伴隨泛型而來的參數化類型的識別問題。
Signature 是其中最重要的一項屬性,作用是存儲一個方法在字節(jié)碼層面的特征簽名,這個屬性中保存的參數類型不是原生類型,而是包括了參數化類型的信息。
<>: 念做typeof
List<E>: E稱為類型參數變量
List<Integer>: Integer稱為實際類型參數
List<Integer>: 整個List<Integer>稱為參數化類型(對應著java.lang.reflect.ParameterizedType接口)
實例化子類中獲取泛型
定義一個MyType
public class MyType<T> {
public Class<T> getTClass() {
return (Class<T>)((ParameterizedType)getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}
}
測試
MyType<String> myType = new MyType<String>(){}; //注意這里是實例化的
// 在類的外部獲取
Type actualTypeArguments = ((ParameterizedType) myType.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
Type rawType = ((ParameterizedType) myType.getClass().getGenericSuperclass()).getRawType();
System.out.println(actualTypeArguments);
System.out.println(rawType);
System.out.println(myType.getTClass());
測試結果
class java.lang.String
class com.james.example.type.MyType
class java.lang.String
未實例化子類無法獲取泛型
測試
MyType<String> myType = new MyType<>(); //注意這里是未實例化的
Type actualTypeArguments = ((ParameterizedType) myType.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
Type rawType = ((ParameterizedType) myType.getClass().getGenericSuperclass()).getRawType();
System.out.println(actualTypeArguments);
System.out.println(rawType);
System.out.println(myType.getTClass());
結果
Exception in thread "main" java.lang.ClassCastException: class java.lang.Class cannot be cast to class java.lang.reflect.ParameterizedType (java.lang.Class and java.lang.reflect.ParameterizedType are in module java.base of loader 'bootstrap')
at com.james.example.type.MyListTest.main(MyListTest.java:23)
List測試
ArrayList<String> list3 = new ArrayList<String>(){};
System.out.println(((ParameterizedType)list3.getClass().getGenericSuperclass()).getRawType());
System.out.println(((ParameterizedType)list3.getClass().getGenericSuperclass()).getActualTypeArguments()[0]);
ArrayList<String> list6 = new ArrayList<>();// 這里new ArrayList<String>() 結果也是同new ArrayList<>()
System.out.println(((ParameterizedType)list6.getClass().getGenericSuperclass()).getRawType());
System.out.println(((ParameterizedType)list6.getClass().getGenericSuperclass()).getActualTypeArguments()[0]);
class java.util.ArrayList
class java.lang.String
class java.util.AbstractList
E
總結
getGenericInterfaces()
getGenericSuperclass()
import java.lang.reflect.ParameterizedType; getActualTypeArguments()
import java.lang.reflect.Type;
Signature屬性的出現,Java泛型擦除法所謂的擦除,只是對方法的Code屬性中的字節(jié)碼進行擦除,實際上元數據中還是保留了泛型信息,這也是我們能通過反射手段獲取參數化類型的根本依據。