Java 泛型(generics)是 JDK 1.5 中引入的一個新特性,泛型提供了編譯時類型安全檢測機制,該機制容許程序在編譯期檢測到非法的類型。java
泛型的本質是參數化類型,也就是說所操做的數據類型被指定爲一個參數。api
參數化類型的意義是將原來具體的類型參數化,相似方法中的變量參數,此時類型也能夠定義成參數形式。數組
好比JDK集合包的List接口,這裏的T能夠稱爲類型形參,在使用/調用時能夠傳入具體的類型(類型實參)。安全
泛型的引入實現了在不建立新的類型的狀況下,經過泛型指定不一樣類型來控制形參具體限制的類型。也就是說在泛型的使用過程當中,操做的數據類型被指定爲了一個類型參數。bash
泛型可使用在類、接口、方法和構造器中。 泛型的聲明 都是在Class類型後面緊跟一個<>符號,在<>內能夠定義1至多個類型參數;泛型可使用統配符 **?**表示無邊界類型,另外可使用 extends 、super 操做符 分別定義泛型的上界或者下界。微信
咱們能夠在Java類上定義泛型,最多見的好比JDK中的集合類 List、Map<K,V>等。框架
public interface List<E> {
boolean add(E e);
}
複製代碼
public class AbstractList<E> extends List<E>{
public boolean add(E e) {
add(size(), e);
return true;
}
}
複製代碼
咱們能夠在方法上定義泛型,此時該泛型的做用域只在該方法中。函數
public class Collections {
public static <T> void sort(List<T> list, Comparator<? super T> c) {
list.sort(c);
}
}
複製代碼
好比JDK中Collections的sort函數在<>內聲明瞭一個泛型T,在函數的第二個參數的類型聲明爲Comparator<? super T>,使用了 super 操做符 對Comparator接口的泛形參數作了下界限制,這樣任何可以處理T類及其父類的Comparator均可以做爲一個合法的參數,拓寬了這個函數的使用場景。工具
無邊界的通配符(Unbounded Wildcards), 就是, 好比Class。無邊界的通配符的主要做用就是讓泛型可以接受未知類型的數據。ui
固定上邊界的通配符(Upper Bounded Wildcards): 使用固定上邊界的通配符的泛型, 能夠指定某個類型及其子類類型. 要聲明使用該類通配符, 採用 <? extends E> 的形式, 這裏的E就是該泛型的上邊界。
固定下邊界的通配符(Lower Bounded Wildcards): 使用固定下邊界的通配符的泛型能夠指代某個類型及其父類類型. 要聲明使用該類通配符, 採用<? super E>的形式, 這裏的E就是該泛型的下邊界。
在引入泛型以前,Java只有所謂的原始類型(raw types),即在此以前全部的類類型均可以經過Class類進行抽象,Class 類的一個具體對象就表明一個指定的原始類型。
在引入泛型以後,爲了將泛型類跟原始類型區分,Java引入了Type類型體系,在新的Type類型體系下,包含了 Class(原生類型)、ParameterizedType(參數化類型)、TypeVariable(類型變量類型)、WildcardType(包含通配符的類型)、GenericArrayType(泛型數組類型)、
ParameterizedType(參數化類型)形如Class<Type...>,好比 Collection 就是一個參數化類型。
TypeVariable 表示的是類型變量類型,好比List類型是參數化類型,這個參數化類型的第1個位置的參數類型是 T,這裏的T類型是類型變量類型。
GenericArrayType表示的是參數化類型或者類型變量的數組類型,形如:A[]或T[]的類型。
包含通配符的類型,形如 ? 、 ? extends Number, ? super Long 好比List<? extends User>類型是一個WildcardType類型。
這裏簡單演示下,由泛型參數帶來的編譯時檢查的特性。
在下面的代碼中,咱們建立了一個List,並添加Integer對象。
public class GenericTest{
public static void main(String[] args) {
List array = new ArrayList<>();
array.add(1);
}
}
複製代碼
由於List接口是一個參數化類型
public interface List<E> extends Collection<E> {
//...
boolean add(E e);
}
複製代碼
咱們可使用泛型改造一下最初的代碼,咱們但願List只接收Integer類型的對象,所以咱們能夠在定義List類型時加上泛型參數
package sample1;
import java.util.ArrayList;
import java.util.List;
public class GenericTest{
public static void main(String[] args) {
List<Integer> array = new ArrayList<>();
array.add("1");
}
}
複製代碼
很明顯 array.add("1") 是一段錯誤的代碼,若是可以正常編譯經過,那麼在運行時可能會帶來意想不到的結果,好比當咱們從List中取出參數時 強轉成成Integer類型會出現類型轉化的異常。
咱們先嚐試使用 javac 編譯文件,幸虧編譯器提早給出了代碼的錯誤
GenericTest.java:10: 錯誤: 對於add(int), 找不到合適的方法
array.add(1);
^
方法 Collection.add(String)不適用
(參數不匹配; int沒法轉換爲String)
方法 List.add(String)不適用
(參數不匹配; int沒法轉換爲String)
方法 AbstractCollection.add(String)不適用
(參數不匹配; int沒法轉換爲String)
方法 AbstractList.add(String)不適用
(參數不匹配; int沒法轉換爲String)
方法 ArrayList.add(String)不適用
(參數不匹配; int沒法轉換爲String)
複製代碼
咱們簡單瞭解了方法中的泛型的使用會在編譯期進行類型檢查,下面咱們進一步研究下在字節碼層面泛型的信息。
在方法體中使用聲明並建立了2個泛型對象
package sample1;
import java.util.ArrayList;
import java.util.List;
public class GenericTest{
public static void main(String[] args) {
List<String> strList = new ArrayList<>();
List<Integer> intList = new ArrayList<>();
}
}
複製代碼
使用javac 命令 編譯該java文件 生成 GnericTest.class
javac GenericTest.java
複製代碼
若是是在Intellij環境下,能夠直接打開該class文件
能夠看到在class文件中,方法內部並不存在泛型信息,咱們能夠簡單的得出結論:在方法體中的泛型信息在編譯後會被擦除。
咱們稍微修改一下代碼,在代碼中嘗試向List 列表中插入數據後取出元素
package sample1;
import java.util.ArrayList;
import java.util.List;
public class GenericTest<T>{
public static void main(String[] args) {
List<String> strList = new ArrayList<>();
List<Integer> intList = new ArrayList<>();
strList.add("hello");
String s = strList.get(0);
}
}
複製代碼
再次編譯該代碼,並使用Intellij打開class文件,此時能夠看到,調用List類型的add方法時,編譯器並無作出任何改動,可是在取出元素時,編譯器會自動強轉成String類型。
讀者能夠經過 javap -v GenericTest 查看 GnericTest類文件能夠看到下面的指令
27: invokeinterface #6, 2 // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
32: checkcast #7 // class java/lang/String
35: astore_3
複製代碼
在上文中 方法中使用泛型的例子中,咱們發如今方法中的泛型在編譯後會被擦除。那麼在泛型類、泛型方法中的泛型信息也會被擦除嗎?實際上Java在引入泛型時,考慮了這個問題,爲了可以在Class文件中保存泛型信息,在JDK1.5後Signature屬性被增長到了Class文件規範中,它是一個可選的定長屬性,能夠出如今類、字段表和方法表結構的屬性表中。任何類、接口、初始化方法或成員的泛型簽名若是包含了類型變量(Type Variables)或參數化類型(Parameterized Types),則Singature屬性會爲它記錄泛型簽名信息。Signature屬性就是爲了彌補擦除法的缺陷而增設的,Java能夠經過反射得到泛型類型,最終的數據來源也就是這個屬性。
編寫一個簡單的泛型示例代碼
import java.util.ArrayList;
import java.util.List;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
public class GenericTest<T extends Number, K > {
private T param1;
private K param2;
public static void main(String[] args) {
List<String> strList = new ArrayList<>();
List<Integer> intList = new ArrayList<>();
strList.add("hello");
String s = strList.get(0);
}
private T genericMethod() {
return null;
}
private T genericMethod1(T p1, K p2,String s) {
return null;
}
public static <T> void sort(List<T> list, Comparator<? super T> c) {
}
}
複製代碼
運行
javac GenericTest.java
編譯生成 GenericTest.class文件
運行
javap -v -p GenericTest
查看GenericTest.class文件中的字節碼信息
如下是生成的GenericTest.class的字節碼信息。
public class GenericTest<T extends java.lang.Number, K extends java.lang.Object> extends java.lang.Object
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #9.#35 // java/lang/Object."<init>":()V
#2 = Class #36 // java/util/ArrayList
#3 = Methodref #2.#35 // java/util/ArrayList."<init>":()V
#4 = String #37 // hello
#5 = InterfaceMethodref #38.#39 // java/util/List.add:(Ljava/lang/Object;)Z
#6 = InterfaceMethodref #38.#40 // java/util/List.get:(I)Ljava/lang/Object;
#7 = Class #41 // java/lang/String
#8 = Class #42 // GenericTest
#9 = Class #43 // java/lang/Object
#10 = Utf8 param1
#11 = Utf8 Ljava/lang/Number;
#12 = Utf8 Signature
#13 = Utf8 TT;
#14 = Utf8 param2
#15 = Utf8 Ljava/lang/Object;
#16 = Utf8 TK;
#17 = Utf8 <init>
#18 = Utf8 ()V
#19 = Utf8 Code
#20 = Utf8 LineNumberTable
#21 = Utf8 main
#22 = Utf8 ([Ljava/lang/String;)V
#23 = Utf8 genericMethod
#24 = Utf8 ()Ljava/lang/Number;
#25 = Utf8 ()TT;
#26 = Utf8 genericMethod1
#27 = Utf8 (Ljava/lang/Number;Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Number;
#28 = Utf8 (TT;TK;Ljava/lang/String;)TT;
#29 = Utf8 sort
#30 = Utf8 (Ljava/util/List;Ljava/util/Comparator;)V
#31 = Utf8 <T:Ljava/lang/Object;>(Ljava/util/List<TT;>;Ljava/util/Comparator<+TT;>;)V
#32 = Utf8 <T:Ljava/lang/Number;K:Ljava/lang/Object;>Ljava/lang/Object;
#33 = Utf8 SourceFile
#34 = Utf8 GenericTest.java
#35 = NameAndType #17:#18 // "<init>":()V
#36 = Utf8 java/util/ArrayList
#37 = Utf8 hello
#38 = Class #44 // java/util/List
#39 = NameAndType #45:#46 // add:(Ljava/lang/Object;)Z
#40 = NameAndType #47:#48 // get:(I)Ljava/lang/Object;
#41 = Utf8 java/lang/String
#42 = Utf8 GenericTest
#43 = Utf8 java/lang/Object
#44 = Utf8 java/util/List
#45 = Utf8 add
#46 = Utf8 (Ljava/lang/Object;)Z
#47 = Utf8 get
#48 = Utf8 (I)Ljava/lang/Object;
{
private T param1;
descriptor: Ljava/lang/Number;
flags: ACC_PRIVATE
Signature: #13 // TT;
private K param2;
descriptor: Ljava/lang/Object;
flags: ACC_PRIVATE
Signature: #16 // TK;
public GenericTest();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 5: 0
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=4, args_size=1
0: new #2 // class java/util/ArrayList
3: dup
4: invokespecial #3 // Method java/util/ArrayList."<init>":()V
7: astore_1
8: new #2 // class java/util/ArrayList
11: dup
12: invokespecial #3 // Method java/util/ArrayList."<init>":()V
15: astore_2
16: aload_1
17: ldc #4 // String hello
19: invokeinterface #5, 2 // InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
24: pop
25: aload_1
26: iconst_0
27: invokeinterface #6, 2 // InterfaceMethod java/util/List.get:(I)Ljava/lang/Object;
32: checkcast #7 // class java/lang/String
35: astore_3
36: return
LineNumberTable:
line 12: 0
line 13: 8
line 14: 16
line 15: 25
line 16: 36
private T genericMethod();
descriptor: ()Ljava/lang/Number;
flags: ACC_PRIVATE
Code:
stack=1, locals=1, args_size=1
0: aconst_null
1: areturn
LineNumberTable:
line 19: 0
Signature: #25 // ()TT;
private T genericMethod1(T, K, java.lang.String);
descriptor: (Ljava/lang/Number;Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Number;
flags: ACC_PRIVATE
Code:
stack=1, locals=4, args_size=4
0: aconst_null
1: areturn
LineNumberTable:
line 23: 0
Signature: #28 // (TT;TK;Ljava/lang/String;)TT;
public static <T extends java.lang.Object> void sort(java.util.List<T>, java.util.Comparator<? extends T>);
descriptor: (Ljava/util/List;Ljava/util/Comparator;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=0, locals=2, args_size=2
0: return
LineNumberTable:
line 28: 0
Signature: #31 // <T:Ljava/lang/Object;>(Ljava/util/List<TT;>;Ljava/util/Comparator<+TT;>;)V
}
Signature: #32 // <T:Ljava/lang/Number;K:Ljava/lang/Object;>Ljava/lang/Object;
SourceFile: "GenericTest.java"
複製代碼
因爲GenericTest類聲明瞭泛型類型 T、K 所以,在打印的字節碼內容尾部有 signature屬性爲該類記錄泛型簽名信息 s
Signature: #33 // <T:Ljava/lang/Number;K:Ljava/lang/Object;>Ljava/lang/Object;
複製代碼
類的成員變量 param1 param2 類型爲泛型,所以 字段信息中會有 **signature屬性記錄該字段的泛型簽名信息
private T param1;
descriptor: Ljava/lang/Number;
flags: ACC_PRIVATE
Signature: #14 // TT;
private K param2;
descriptor: Ljava/lang/Object;
flags: ACC_PRIVATE
Signature: #17 // TK;
複製代碼
這裏的TT TK 第一個T表示該類型是泛型類型,T後面跟隨着泛型的標識。
類的成員方法 genericMethod()方法返回類型爲泛型 和 genericMethod1(T p1, K p2) 方法的參數及返回類型爲泛型類,所以,在字節碼中這兩個方法的信息中也會有Signature記錄該方法包含泛型的簽名信息。
private T genericMethod();
descriptor: ()Ljava/lang/Number;
flags: ACC_PRIVATE
Code:
stack=1, locals=1, args_size=1
0: aconst_null
1: areturn
LineNumberTable:
line 19: 0
Signature: #26 // ()TT;
private T genericMethod1(T, K, java.lang.String);
descriptor: (Ljava/lang/Number;Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Number;
flags: ACC_PRIVATE
Code:
stack=1, locals=4, args_size=4
0: aconst_null
1: areturn
LineNumberTable:
line 23: 0
Signature: #29 // (TT;TK;Ljava/lang/String;)TT;
複製代碼
方法的泛型簽名由兩部分組成,分別是方法參數和方法返回類型;下面會簡單的解析下這兩個方法的簽名組成. 首先方法的簽名信息由兩部分組成,分別是方法的參數部分和返回類型部分。
1.genericMethod方法 private T genericMethod()
genericMethod()方法的簽名信息爲 ()TT;
方法參數部分的簽名爲(),由於該方法沒有任何參數
方法的返回類型爲泛型類型T,所以方法返回類型部分的簽名爲TT
2.genericMethod1方法
private T genericMethod1(T p1, K p2,String s)
該方法的參數部分簽名爲**(TT;TK;Ljava/lang/String;),由於方法分別有3個參數,其中泛型化類型T在方法簽名中的標識爲 TT ,泛型類型K的標識爲 TK ,String類型爲 Ljava/lang/String,類型直接以「;」做爲分隔。(引用數據類型在字節碼中的標識的格式爲LClassName**,其中ClassName爲類的全路徑名稱,全路徑名稱中的「.」號由「/」代替,最終方法簽名的參數部分爲 (TT;TK;Ljava/lang/String;)。
該方法的返回類型爲泛型T,所以返回類型部分爲TT
泛型靜態方法中,泛型的類型不能直接引用類上的泛型類型,靜態方法若是要使用泛型,必須本身定義泛型類型,好比這裏 sort方法的泛型類型T是在經過方法的 聲明的,這裏的泛型類型T跟類的上的泛型類型T並非同一個。
咱們看下該靜態方法的簽名信息
public static <T> void sort(List<T> list, Comparator<? super T> c) {
list.sort(c);
}
複製代碼
public static <T extends java.lang.Object> void sort(java.util.List<T>, java.util.Comparator<? super T>);
descriptor: (Ljava/util/List;Ljava/util/Comparator;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: aload_1
2: invokeinterface #8, 2 // InterfaceMethod java/util/List.sort:(Ljava/util/Comparator;)V
7: return
LineNumberTable:
line 28: 0
line 29: 7
Signature: #32 // <T:Ljava/lang/Object;>(Ljava/util/List<TT;>;Ljava/util/Comparator<-TT;>;)V
複製代碼
簽名信息爲
<T:Ljava/lang/Object;>(Ljava/util/List<TT;>;Ljava/util/Comparator<-TT;>;)V
注意這裏簽名信息的開頭部分,在函數參數的標識部分的左邊提早描述了泛型類型T的簽名信息 <T:Ljava/lang/Object;>。 另外函數參數 Comparator<? super T>在方法簽名中的標識爲Ljava/util/Comparator<-TT;>,若是這裏的super變爲extends則簽名信息變爲Ljava/util/Comparator<+TT;>。由於super操做符在字節碼中的標識爲符號 -,extends操做符在字節碼中的標識爲符號 +。
咱們已經介紹了泛型的相關類型,以及泛型在字節碼中的信息。爲了在運行時獲取泛型的類型,Java提供相應的api實現。
首先咱們上文介紹了,包括原生類型及泛型類型,全部的類型均可以用Type接口表示。 所以我寫了一個工具方法打印全部具體Type類型的具體信息。有關這些Type類型的具體api信息能夠直接查看jdk上這些類的註釋。
public static void printTypeInfo(Type argumentType) {
if (argumentType instanceof Class<?>) {
System.out.println("原生類型-> " + ((Class) argumentType).getCanonicalName());
} else if (argumentType instanceof ParameterizedType) {
System.out.println("參數化類型-> " + ((ParameterizedType) argumentType).toString());
Type[] actualTypeArguments = ((ParameterizedType) argumentType).getActualTypeArguments();
System.out.println(" 參數化類型中的泛型參數信息以下:");
for (int i = 0; i < actualTypeArguments.length; i++) {
Type actualTypeArgument = actualTypeArguments[i];
System.out.println(" 第" + i + "個泛型參數類型爲");
System.out.print(" ");
printTypeInfo(actualTypeArgument);
}
} else if (argumentType instanceof GenericArrayType) {
System.out.println("泛型類型數組-> " + ((GenericArrayType) argumentType).toString());
System.out.print(" 泛型類型數組存儲的類型爲");
printTypeInfo(((GenericArrayType) argumentType).getGenericComponentType());
} else if (argumentType instanceof TypeVariable) {
System.out.println("類型變量類型-> " + ((TypeVariable) argumentType).getName());
} else if (argumentType instanceof WildcardType) {
System.out.println("包含通配符的類型-> " + ((WildcardType) argumentType).toString());
Type[] upperBounds = ((WildcardType) argumentType).getUpperBounds();
if (upperBounds != null) {
System.out.println(" 通配符的上界包括:");
for (int j = 0; j < upperBounds.length; j++) {
System.out.println(" " + upperBounds[j].getTypeName());
}
}
Type[] lowerBounds = ((WildcardType) argumentType).getLowerBounds();
if (lowerBounds != null) {
System.out.println(" 通配符的下界包括:");
for (int j = 0; j < lowerBounds.length; j++) {
System.out.println(" " + lowerBounds[j].getClass());
}
}
}
}
複製代碼
如下這些api獲取的泛型數據的來源就是class文件字節碼中 類及方法上的signature的信息。
Class類提供了getGenericSuperclass、getGenericInterfaces方法
Method類提供了獲取方法泛型信息的兩個api爲 getGenericReturnType、getGenericParameterTypes
getGenericReturnType() 得到方法的方法類型對應的type
getGenericParameterTypes() 得到方法上全部參數的泛型類型信息,由於方法參數可能有多個,因此這個方法的返回值是一個 Type數組。
以前寫過一篇Retrofit框架的文章,簡單的介紹了Retrofit庫的組成部分,其中Retrofit一個很優雅的部分就是內部自動會跟你局接口方法所定義的Model類型自動作JSON類型轉換。
咱們能夠簡單演示下這裏的泛型應用場景,獲取方法返回類型上的泛型參數的具體類型。
在下面的例子中,我簡單仿寫了使用了Retrofit庫在項目中的工程代碼,在main方法打印Service接口中方法的返回泛型類型。
import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
public class GenericTest{
static interface Call<T> {
}
static class User {
}
static class Post {
}
static interface Service {
Call<User> login(String account, String password);
Call<List<Post>> getPosts();
}
public static void main(String[] args) {
System.out.println("開始打印Service接口的方法信息");
Method[] methods = Service.class.getMethods();
for (int i = 0; i < methods.length; i++) {
Method method = methods[i];
//方法的getReturnType返回的是Class類型,若是但願可以獲取到泛型信息,則應該使用JDK 1.5 以後的 getGenericReturnType方法
Class<?> returnType = method.getReturnType();
Type genericReturnType = method.getGenericReturnType();
System.out.print(method.getName() + "方法的返回");
printTypeInfo(genericReturnType);
System.out.println();
}
}
public static void printTypeInfo(Type argumentType) {
if (argumentType instanceof Class<?>) {
System.out.println("原生類型-> " + ((Class) argumentType).getCanonicalName());
} else if (argumentType instanceof ParameterizedType) {
System.out.println("參數化類型-> " + ((ParameterizedType) argumentType).toString());
Type[] actualTypeArguments = ((ParameterizedType) argumentType).getActualTypeArguments();
System.out.println(" 參數化類型中的泛型參數信息以下:");
for (int i = 0; i < actualTypeArguments.length; i++) {
Type actualTypeArgument = actualTypeArguments[i];
System.out.println(" 第" + i + "個泛型參數類型爲");
System.out.print(" ");
printTypeInfo(actualTypeArgument);
}
} else if (argumentType instanceof GenericArrayType) {
System.out.println("泛型類型數組-> " + ((GenericArrayType) argumentType).toString());
System.out.print(" 泛型類型數組存儲的類型爲");
printTypeInfo(((GenericArrayType) argumentType).getGenericComponentType());
} else if (argumentType instanceof TypeVariable) {
System.out.println("類型變量類型-> " + ((TypeVariable) argumentType).getName());
} else if (argumentType instanceof WildcardType) {
System.out.println("包含通配符的類型-> " + ((WildcardType) argumentType).toString());
Type[] upperBounds = ((WildcardType) argumentType).getUpperBounds();
if (upperBounds != null) {
System.out.println(" 通配符的上界包括:");
for (int j = 0; j < upperBounds.length; j++) {
System.out.println(" " + upperBounds[j].getTypeName());
}
}
Type[] lowerBounds = ((WildcardType) argumentType).getLowerBounds();
if (lowerBounds != null) {
System.out.println(" 通配符的下界包括:");
for (int j = 0; j < lowerBounds.length; j++) {
System.out.println(" " + lowerBounds[j].getClass());
}
}
}
}
}
複製代碼
執行結果中能夠看到咱們最終獲取了方法的返回類型上的泛型參數中的原生類型 User類及Post類,拿到具體的類型後,Retrofit框架內部就能夠作Json轉換了(實際上大部分Json轉換庫如Gson只須要獲取到Type類型就能夠了)。
開始打印Service接口的方法信息
login方法的返回類型爲參數化類型-> GenericTest.GenericTest$Call<GenericTest$User>
參數化類型中的泛型參數信息以下:
第0個泛型參數類型爲
原生類型-> GenericTest.User
getPosts方法的返回類型爲參數化類型-> GenericTest.GenericTest$Call<java.util.List<GenericTest$Post>>
參數化類型中的泛型參數信息以下:
第0個泛型參數類型爲
參數化類型-> java.util.List<GenericTest$Post>
參數化類型中的泛型參數信息以下:
第0個泛型參數類型爲
原生類型-> GenericTest.Post
複製代碼
在全文中咱們首先簡單介紹了泛型的概念、泛型的類型系統,以後從字節碼角度解讀了泛型在字節碼層面的信息,最後回到應用層,咱們介紹了泛型相關的反射api,並使用這些api完成一個小需求。
Java 泛型的引入爲開發者在不少場景下帶來了不少的靈活度。若是但願進一步的掌握泛型的使用,一方面能夠在平時的開發過程當中思考在哪些場景下能夠引入泛型參數,活學活用;另外一方面能夠研究系統的類庫好比Class類、集合類中泛型的使用,也能夠研究一些開源框架好比Retrofit庫。
關注微信公衆號 查看個人更多文章
![]()