不太完美的泛型
大家在使用泛型開發(fā)的時候,有很多時候會遇到這樣的需求—— 獲取泛型具體的類。
比如現(xiàn)在有Service<T>,要想得到泛型T的類型,正常情況下,我們第一個想法就是通過T.class獲得,但得到的是那刺眼的下劃紅色波浪線。
那么如何才能拿到泛型的類型?想要得到泛型的類型需要滿足哪些條件呢?
獲取到泛型類型的條件
- 必須具有真實類型的存在
- 泛型的類型是明確的
第一個很好理解,如果連要獲取的類都不存在,即未定義,那自然是獲取不到的;
第二個條件,舉個例子,假設存在User,那么List<User>就是明確的,List<T>是不明確的。
滿足上面兩點,就可以獲取泛型的類型了。
獲取泛型類型
首先看下java.lang.Class.java中與泛型相關的方法有哪些:

其中
getGenericInfo()和getGenericSignature0()都是私有方法,那么我們暫時就只能嘗試從getGenericInterfaces()和getGenericSuperclass()這兩個方法入手了。
查看源碼上的注釋,大致作用如下:
- getGenericInterfaces: 返回此類直接實現(xiàn)的所有接口的類型。接口的實現(xiàn)類通過該方法,可以獲取所實現(xiàn)的接口的泛型類型,比如:接口
Service<T>,實現(xiàn)類UserServiceImpl<User>,能夠得到Service<User> - getGenericSuperclass: 返回此類所表示的實體的直接超類的類型。一個類的子類通過該方法,可以獲取超類的泛型類型,比如:
SuperClass<T>,子類Main<User>,能夠得到SuperClass<User>
可以看出都跟繼承類/實現(xiàn)接口有關,因為在類或接口定義的類型參數(shù)(泛型)其實是不確定的,只有子類繼承或接口實現(xiàn)才能確定類型參數(shù)的具體類型。比如:接口Service<T>,類型參數(shù)T是不確定的,說白了其實就是個占位符,而實現(xiàn)類UserServiceImpl<User>才能確定類型參數(shù)T是User。
為了更好理解,先來看一段代碼:
package xxx.xxx.xxx;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
abstract class SuperClass<T> {
}
class Main extends SuperClass<User> {
public static void main(String[] args) {
// 獲取 Main 的超類 SuperClass 的簽名(攜帶泛型). 這里為: xxx.xxx.xxx.SuperClass<xxx.xxx.xxx.User>
Type genericSuperclass = Main.class.getGenericSuperclass();
// 強轉成 參數(shù)化類型 實體.
ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
System.out.println(parameterizedType);
// 獲取超類的泛型類型數(shù)組. 即SuperClass<User>的<>中的內容, 因為泛型可以有多個, 所以用數(shù)組表示
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
Type genericType = actualTypeArguments[0];
Class<User> clazz = (Class<User>) genericType;
System.out.println(clazz);
}
}
class User {
}
打印結果:
xxx.xxx.xxx.SuperClass<xxx.xxx.xxx.User>
class xxx.xxx.xxx.User
上面的代碼,是通過繼承方式,明確類型,然后獲取泛型類。
總體思路是這樣的:先定義一個類SuperClass<T>,其中T為類型參數(shù),即泛型,這個時候要想獲取T的類型,顯然是不可能的,因為本來就是一個通用類型;但是通過繼承該類,并確定了泛型的類型,那么我們就能通過getGenericSuperclass()獲取到泛型的類型。
其實,上面的代碼只是為了方便測試才這么寫的,也比較容易理解,一般實戰(zhàn)的寫法是這樣的:
abstract class SuperClass<T> {
public Class<T> getGenericClass() {
ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
return (Class<T>) parameterizedType.getActualTypeArguments()[0];
}
}
class Main extends SuperClass<User> {
public static void main(String[] args) {
System.out.println(new Main().getGenericClass());
}
}
class User {
}
不出意外的話,會打印如下結果:
class xxx.xxx.xxx.User
需要注意的是,SuperClass<T>是一個抽象類,因為getGenericClass()方法是為它的子類服務的,SuperClass<T>本身的實例對象如果調用該方法,將會直接報錯或返回錯誤的結果,所以干脆定義成抽象類,避免被實例化。另外,為了防止方法被覆寫,可以使用private訪問修飾符或者定義為 final 方法。
當然多級繼承也是適用的,比如:
// 這里省略 SuperClass 和 User 的定義
class Main<T> extends SuperClass<T> {
}
class MultiInheritance extends Main<User> {
public static void main(String[] args) {
System.out.println(new MultiInheritance().getGenericClass());
}
}
通過上面的代碼,基本可以了解通過getGenericSuperClass()獲取泛型類型的大概思路和原理,而getGenericInterfaces()的獲取思路也是大同小異。直接上實例代碼:
package xxx.xxx.xxx;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
interface ServiceA<T> {
}
interface ServiceB<T> {
}
class ServiceImpl implements ServiceA<EntityA>, ServiceB<EntityB> {
public static void main(String[] args) {
Type[] genericInterfaces = ServiceImpl.class.getGenericInterfaces();
if (genericInterfaces.getClass().isAssignableFrom(ParameterizedType[].class)) {
for (Type genericInterface : genericInterfaces) {
ParameterizedType parameterizedType = (ParameterizedType) genericInterface;
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
Type type = actualTypeArguments[0];
if (type instanceof Class) {
Class<?> clazz = (Class<?>) type;
System.out.println(clazz);
}
}
}
}
}
class EntityA {
}
class EntityB {
}
控制臺打印結果如下:
class xxx.xxx.xxx.EntityA
class xxx.xxx.xxx.EntityB
擴展
很多情況下,Class被用來當作參數(shù),我們其實可以將帶泛型的類作為參數(shù)傳入,我們一般為了方便,很少去特定定義一個類,因此,我們可以借助匿名內部類的便利來達到這一目的。
首先看一段代碼:
package xxx.xxx.xxx;
import java.lang.reflect.ParameterizedType;
abstract class SuperClass<T> {
public Class<T> getGenericClass() {
ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
return (Class<T>) parameterizedType.getActualTypeArguments()[0];
}
public static void main(String[] args) {
System.out.println(new SuperClass<User>(){}.getGenericClass());
}
}
其中SuperClass<User>(){}就是一個匿名內部類。當然上面只是為了展示匿名內部類,真正的應用可以參考fastjson的TypeRefrence。