Java中獲取泛型類型信息的方法
根據(jù)使用泛型位置的不同可以分為:聲明側(cè)泛型、使用側(cè)泛型。
聲明側(cè)的泛型信息被記錄在Class文件的Constant pool中以Signature的形式保存。而使用側(cè)的泛型信息并沒(méi)有保存。
聲明側(cè)泛型
聲明側(cè)泛型包括:
- 泛型類,或泛型接口的聲明
- 帶有泛型參數(shù)的成員變量
- 帶有泛型參數(shù)的方法
使用側(cè)泛型
使用側(cè)泛型包括:
- 方法的局部變量,
- 方法調(diào)用時(shí)傳入的變量
獲取泛型類型相關(guān)方法
上文有提到,聲明側(cè)的泛型被記錄在Class文件的Constant pool中以Signature的形式保存。
JDK的Class、Field、Method類提供了一系列的獲取泛型類型的相關(guān)方法。
1. Class類的泛型方法
Type getGenericSuperclass():獲取父類的Type
- 若父類有泛型,返回的實(shí)際Type是ParameterizedType接口的實(shí)現(xiàn)類ParameterizedTypeImpl類
- 若父類無(wú)泛型,返回的實(shí)際Type是Class類
Type[] getGenericInterfaces():獲取父接口的Type集合
- 若父類有泛型,返回的實(shí)際Type是ParameterizedType接口的實(shí)現(xiàn)類ParameterizedTypeImpl類
- 若父類無(wú)泛型,返回的實(shí)際Type是Class類
2. Field類的泛型方法
Type getGenericType():獲取字段的Type
- 若字段有泛型,返回的實(shí)際Type是ParameterizedType接口的實(shí)現(xiàn)類ParameterizedTypeImpl類
- 若字段無(wú)泛型,返回的實(shí)際Type是Class類
3. Method類的泛型方法
Type getGenericReturnType():獲取方法返回值的Type
- 若返回值有泛型,返回的實(shí)際Type是ParameterizedType接口的實(shí)現(xiàn)類ParameterizedTypeImpl類
- 若返回值無(wú)泛型,返回的實(shí)際Type是Class類
Type[] getGenericParameterTypes():獲取方法參數(shù)的Type集合
- 若方法參數(shù)有泛型,返回的實(shí)際Type是ParameterizedType接口的實(shí)現(xiàn)類ParameterizedTypeImpl類
- 若方法參數(shù)無(wú)泛型,返回的實(shí)際Type是Class類
Type[] getGenericExceptionTypes():獲取方法聲明的異常的Type集合
- 若方法參數(shù)有泛型,返回的實(shí)際Type是ParameterizedType接口的實(shí)現(xiàn)類ParameterizedTypeImpl類
- 若方法參數(shù)無(wú)泛型,返回的實(shí)際Type是Class類
4. ParameterizedType類
ParameterizedType是Type的子接口,表示參數(shù)化類型,用于獲取泛型的參數(shù)類型。
ParameterizedType的主要方法:
- Type[] getActualTypeArguments():獲取實(shí)際類型參數(shù)的Type集合
- Type getRawType():獲取聲明此類型的類或接口的Type
- Type getOwnerType():如果聲明此類型的類或接口為內(nèi)部類,這返回的是該內(nèi)部類的外部類的Type(也就是該內(nèi)部類的擁有者)
獲取聲明側(cè)的泛型類型信息
- 泛型類,或泛型接口的聲明
- 帶有泛型參數(shù)的成員變量
- 帶有泛型參數(shù)的方法
示例:
public class MyTest extends TestClass<String> implements TestInterface1<Integer>,TestInterface2<Long> { ? ? private List<Integer> list; ? ? private Map<Integer, String> map; ? ? public List<String> aa() { ? ? ? ? return null; ? ? } ? ? public void bb(List<Long> list) { ? ? } ? ? public static void main(String[] args) throws Exception { ? ? ? ? System.out.println("======================================= 泛型類聲明的泛型類型 ======================================="); ? ? ? ? ParameterizedType parameterizedType = (ParameterizedType)MyTest.class.getGenericSuperclass(); ? ? ? ? System.out.println(parameterizedType.getTypeName() + "--------->" + parameterizedType.getActualTypeArguments()[0].getTypeName()); ? ? ? ? Type[] types = MyTest.class.getGenericInterfaces(); ? ? ? ? for (Type type : types) { ? ? ? ? ? ? ParameterizedType typ = (ParameterizedType)type; ? ? ? ? ? ? System.out.println(typ.getTypeName() + "--------->" + typ.getActualTypeArguments()[0].getTypeName()); ? ? ? ? } ? ? ? ? System.out.println("======================================= 成員變量中的泛型類型 ======================================="); ? ? ? ? ParameterizedType parameterizedType1 = (ParameterizedType)MyTest.class.getDeclaredField("list").getGenericType(); ? ? ? ? System.out.println(parameterizedType1.getTypeName() + "--------->" + parameterizedType1.getActualTypeArguments()[0].getTypeName()); ? ? ? ? ParameterizedType parameterizedType2 = (ParameterizedType)MyTest.class.getDeclaredField("map").getGenericType(); ? ? ? ? System.out.println(parameterizedType2.getTypeName() + "--------->" + parameterizedType2.getActualTypeArguments()[0].getTypeName()+","+parameterizedType2.getActualTypeArguments()[1].getTypeName()); ? ? ? ? System.out.println("======================================= 方法參數(shù)中的泛型類型 ======================================="); ? ? ? ? ParameterizedType parameterizedType3 = (ParameterizedType)MyTest.class.getMethod("aa").getGenericReturnType(); ? ? ? ? System.out.println(parameterizedType3.getTypeName() + "--------->" + parameterizedType3.getActualTypeArguments()[0].getTypeName()); ? ? ? ? System.out.println("======================================= 方法返回值中的泛型類型 ======================================="); ? ? ? ? Type[] types1 = MyTest.class.getMethod("bb", List.class).getGenericParameterTypes(); ? ? ? ? for (Type type : types1) { ? ? ? ? ? ? ParameterizedType typ = (ParameterizedType)type; ? ? ? ? ? ? System.out.println(typ.getTypeName() + "--------->" + typ.getActualTypeArguments()[0].getTypeName()); ? ? ? ? } ? ? } } class TestClass<T> { } interface TestInterface1<T> { } interface TestInterface2<T> { }
輸出
======================================= 泛型類聲明的泛型類型 =======================================
com.joker.test.generic.TestClass<java.lang.String>--------->java.lang.String
com.joker.test.generic.TestInterface1<java.lang.Integer>--------->java.lang.Integer
com.joker.test.generic.TestInterface2<java.lang.Long>--------->java.lang.Long
======================================= 成員變量中的泛型類型 =======================================
java.util.List<java.lang.Integer>--------->java.lang.Integer
java.util.Map<java.lang.Integer, java.lang.String>--------->java.lang.Integer,java.lang.String
======================================= 方法參數(shù)中的泛型類型 =======================================
java.util.List<java.lang.String>--------->java.lang.String
======================================= 方法返回值中的泛型類型 =======================================
java.util.List<java.lang.Long>--------->java.lang.Long
獲取使用側(cè)的泛型類型信息
上面講的相關(guān)類的獲取泛型類型相關(guān)方法都只是針對(duì)聲明側(cè)的泛型。因?yàn)槁暶鱾?cè)的泛型被記錄在Class文件的Constant pool中以Signature的形式保存。所以Java提供了相關(guān)方法能獲取到這些信息。
那使用側(cè)的泛型信息怎么獲取呢?由于使用側(cè)的泛型信息在編譯期的時(shí)候就被類型擦除了,所以運(yùn)行時(shí)是沒(méi)辦法獲取到這些泛型信息的。
難道就真的沒(méi)辦法了嗎,其實(shí)還是有的。使用側(cè)需要獲取泛型信息的地方主要是:方法調(diào)用時(shí)傳入的泛型變量,通常需要在方法中獲取變量的泛型類型。比如在JSON解析(反序列化)的場(chǎng)景,他們是怎么實(shí)現(xiàn)的了。
針對(duì)獲取使用側(cè)的泛型類型信息,主要實(shí)現(xiàn)方案是通過(guò)匿名內(nèi)部類。
Gson中的泛型抽象類TypeToken<T>,F(xiàn)astJson中的泛型類TypeReference<T>等就是用的該方案。
匿名內(nèi)部類實(shí)現(xiàn)獲取使用側(cè)的泛型類型
上文有講到,在聲明側(cè)的泛型中,針對(duì)泛型類或泛型接口的聲明的泛型,Class類提供了getGenericSuperclass()、getGenericInterfaces()來(lái)獲取其子類(實(shí)現(xiàn)類)上聲明的具體泛型類型信息。
而匿名內(nèi)部類是什么?其本質(zhì)就是一個(gè)繼承/實(shí)現(xiàn)了某個(gè)類(接口,普通類,抽象類)的子類匿名對(duì)象。
匿名內(nèi)部類實(shí)現(xiàn)獲取使用側(cè)的泛型類型的原理:
- 定義泛型類,泛型類中有一個(gè)Type類型的字段,用于保存泛型類型的Type
- 通過(guò)匿名內(nèi)部類的方式創(chuàng)建該泛型類的子類實(shí)例(指定了具體的泛型類型)
在創(chuàng)建子類實(shí)例的構(gòu)造方法中,已經(jīng)通過(guò)子類的Class的getGenericSuperclass()獲取到了泛型類型信息并復(fù)制給了Type類型的字段中。 - 隨后任何地方,只要得到了該子類實(shí)例,就可以通過(guò)實(shí)例得到泛型類型的Type,這就得到了使用側(cè)的泛型類信息。
簡(jiǎn)單示例:
定義泛型類TestClass2<T>,類中包含字段Type
public abstract class TestClass2<T> { ? ? private final Type type; ? ? public TestClass2() { ? ? ? ? Type superClass = getClass().getGenericSuperclass(); ? ? ? ? if (!(superClass instanceof ParameterizedType)) { ? ? ? ? ? ? throw new IllegalArgumentException("無(wú)泛型類型信息"); ? ? ? ? } ? ? ? ? type = ((ParameterizedType) superClass).getActualTypeArguments()[0]; ? ? } ? ? public Type getType() { ? ? ? ? return type; ? ? } }
測(cè)試獲取泛型類型
public class Test { ? ? public static ?<T> T get(TestClass2<T> tTestClass2) throws IllegalAccessException, InstantiationException { ? ? ? ? Type type = tTestClass2.getType(); ? ? ? ? Class clazz = (Class) type; ? ? ? ? return (T)clazz.newInstance(); ? ? } ? ? public static void main(String[] args) throws InstantiationException, IllegalAccessException { ? ? ? ? String str = get(new TestClass2<String>() {}); ? ? ? ? Date date = get(new TestClass2<Date>() {}); ? ? } }
到此這篇關(guān)于Java中獲取泛型類型信息的方法的文章就介紹到這了,更多相關(guān)Java獲取泛型類型信息內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spark學(xué)習(xí)筆記(一)Spark初識(shí)【特性、組成、應(yīng)用】
這篇文章主要介紹了Spark學(xué)習(xí)筆記之Spark初識(shí),簡(jiǎn)單分析了spark四大特性、基本組成、應(yīng)用場(chǎng)景,需要的朋友可以參考下2020-02-02基于Spring AOP proxyTargetClass的行為表現(xiàn)總結(jié)
這篇文章主要介紹了Spring AOP proxyTargetClass的行為表現(xiàn)總結(jié),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08springboot接口參數(shù)為L(zhǎng)ist的問(wèn)題
這篇文章主要介紹了springboot接口參數(shù)為L(zhǎng)ist的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11通過(guò)實(shí)例解析java String不可變性
這篇文章主要介紹了通過(guò)實(shí)例解析java String不可變性,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03Spring Boot接收單個(gè)String入?yún)⒌慕鉀Q方法
這篇文章主要給大家介紹了關(guān)于Spring Boot接收單個(gè)String入?yún)⒌慕鉀Q方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用spring boot具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-11-11java獲取redis日志信息與動(dòng)態(tài)監(jiān)控信息的方法
這篇文章主要給大家介紹了關(guān)于java如何獲取redis日志信息與動(dòng)態(tài)監(jiān)控信息的方法,文中介紹的非常詳細(xì),對(duì)大家具有一定的參考價(jià)值,需要的朋友們下面來(lái)一起看看吧。2017-04-04SpringBoot深入探究四種靜態(tài)資源訪問(wèn)的方式
這一節(jié)詳細(xì)的學(xué)習(xí)一下SpringBoot的靜態(tài)資源訪問(wèn)相關(guān)的知識(shí)點(diǎn)。像這樣的知識(shí)點(diǎn)還挺多,比如SpringBoot2的Junit單元測(cè)試等等。本章我們來(lái)了解靜態(tài)資源訪問(wèn)的四種方式2022-05-05JavaSE實(shí)現(xiàn)電影院系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了JavaSE實(shí)現(xiàn)電影院系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-08-08