Java泛型和反射

1. 字節碼對象的三種獲取方式

以String爲例html

Class<? extends String> strCls = "".getClass();
Class<String> strCls2 = String.class;
Class strCls3 = Class.forName("java.lang.String");

System.out.println(strCls.equals(strCls2)); // true
System.out.println(strCls.equals(strCls3)); // true

對於第一種方式:經過一個String實例的getClass方法來獲取,這個函數的簽名以下:java

public final native Class<?> getClass();

但文檔中對這個函數的解釋以下:express

The actual result type is Class<? extends |X|> where |X| is the erasure of the static type of the expression on which getClass is called. For example, no cast is required in this code fragment:
Number n = 0; 
Class<? extends Number> c = n.getClass();數組

因此上面就要用Class<? extends String>來接收返回值了。函數

對於第三種方式 forName的返回值爲 Class<?>,這個等價於Class。例如 List 同 List<?> 是同樣的。 測試

三種方式獲取到的返回值都是同樣的,由於String的字節碼對象也就只有一個。ui

2. 泛型與反射

http://www.javashuo.com/article/p-cmivtvcj-a.htmlthis

2.1獲取父類/接口中的泛型信息

static class DemoClassParent <T> {
    private Map<X, T> xs;
    private void show(Map<X, T> data, int y){}
}

static class DemoClassB extends DemoClassParent<Integer> {}

Type type = DemoClassB.class.getGenericSuperclass();
//        clazz.getGenericInterfaces()

if(type instanceof ParameterizedType){
    ParameterizedType pType = (ParameterizedType) type;

    // 若是父類泛型有多個,則這裏會循環屢次
    for (Type type2 : pType.getActualTypeArguments()) {
        if(type2 instanceof Class){
            Class target = (Class) type2;
            System.out.println(target.getName()); 
        }
    }
}

// 輸出 java.lang.Integer

 

2.2 獲取普通類的成員泛型和函數參數泛型 

static class X {}
static class DemoClassA {
    private Map<String, X> xs;
    private int kkk;
    private void show(Map<String, X> data, int y){}
}

static void ayalyse(Type gType){
    if(gType instanceof Class){
        System.out.println("非泛型類型:" + gType.getTypeName());
        return;
    }
    if(!(gType instanceof ParameterizedType)){
        return;
    }
    ParameterizedType pType = (ParameterizedType) gType;
    System.out.println("泛型參數類型:" + pType.getRawType().getTypeName());

    Type[] actualTypeArguments = pType.getActualTypeArguments();
    for (Type type2 : actualTypeArguments) {
        if(type2 instanceof Class){
            Class target = (Class) type2;
            System.out.println(target.getName());
        }
    }
}

static void test(Class clazz){
    System.out.println("---- 反射成員屬性");
    for(Field field:clazz.getDeclaredFields()){
        Type t = field.getGenericType();
        ayalyse(t);
    }

    System.out.println("---- 反射成員函數");
    for(Method method : clazz.getDeclaredMethods()){
        // 獲取函數參數類型
        for (Type type : method.getGenericParameterTypes()) {
            ayalyse(type);
        }
    }
}
public static void main(String[] args) {
    test(DemoClassA.class);
}

 

運行結果:spa

---- 反射成員屬性
泛型參數類型:java.util.Map
java.lang.String
com.example.demo.DemoApplication$X
非泛型類型:int
---- 反射成員函數
泛型參數類型:java.util.Map
java.lang.String
com.example.demo.DemoApplication$X
非泛型類型:intcode

2.3Type接口

可見,以上例子的關鍵在於解析反射出來的type接口子類。Type接口的子類有5個:ParameterizedType,TypeVariable,GenericArrayType,Class,WildcardType。(後續說的T,都是泛型類上的泛型參數T)

ParameterizedType

表明了包含泛型信息的類型。如List<String>,List<T>,DemoA<String>等

public interface ParameterizedType extends Type {
   //獲取<>中的實際類型
    Type[] getActualTypeArguments();
   //獲取<>前的實際類型
    Type getRawType();
  //若是當前類型對應的類是內部類,則返回這個內部類對應的外部類的類型,不然返回null
    Type getOwnerType();
}

TypeVariable

表明了不包含泛型信息的類型(不包括基本類型,如int),即定義中沒有<>的類,有T,Object等。

public interface TypeVariable<D extends GenericDeclaration> extends Type, AnnotatedElement {

    // 獲取泛型類型的上限,若是沒有上限(即 class Person<T>{},這裏的類型變量T 沒有上限),那麼上限爲Object
    Type[] getBounds();

    // 獲取聲明該類型變量的類好比( TypeTest<T> 中的TypeTest )
    D getGenericDeclaration();

    // 獲取類型變量在源碼中定義的名稱,如T
    String getName();

    AnnotatedType[] getAnnotatedBounds();
}

 

演示一下前三個方法:

public class TypeTest<T extends String & Comparable<String>> {//繼承String,實現接口Comparable<String>,能夠用&鏈接多個接口
    public static void main(String[] args) throws NoSuchFieldException, SecurityException, NoSuchMethodException {
        TypeVariable tv[] = TypeTest.class.getTypeParameters();
        Type[] ts = tv[0].getBounds();
        for( Type t : ts ){
            System.out.println( t );
        }
    }
}

輸出:
class java.lang.String
java.lang.Comparable<java.lang.String>

public class TypeTest<T> {
    public static void main(String[] args) throws NoSuchFieldException, SecurityException, NoSuchMethodException {
        TypeVariable tv[] = TypeTest.class.getTypeParameters();
        System.out.println( tv[0].getGenericDeclaration() );
        System.out.println( tv[0].getName() );
    }
}

輸出:
class testProject.TypeTest
T

 

GenericArrayType

表明了泛型數組類型。如List<T>[],List<String>[],T[],DemoA<T>[],DemoA<String>[]等。

public interface GenericArrayType extends Type {
    Type getGenericComponentType();
}

 

這個函數返回泛型數組中元素的Type類型,即List<String>[] 中的 List<String>(ParameterizedTypeImpl),T[] 中的T(TypeVariableImpl),List<String>[][]中的List<String>[]();

Class

表明了泛型參數。如List,DemoA,String,List[],int[],int.

public final class Class<T> extends Object
implements Serializable,GenericDeclaration,Type,AnnotatedElement

 

getTypeParameters方法:返回當前類的泛型信息

class People<T,V,S>{
    
}
class Chinese extends People<String,Integer,Double>{
    
}

TypeVariable[] tv = People.class.getTypeParameters();
System.out.println( tv.length );
for( TypeVariable t : tv ){
    System.out.println( t );
}
TypeVariable[] tv1 = Chinese.class.getTypeParameters();
System.out.println( tv1.length ); 

輸出:
3
T
V
S
0

 

getGenericSuperClass:獲取父類的泛型信息。

如( class Chinese extendis People<String,Integer,Double>{},返回的是People<String,Integer,Double>,若是沒有父類,返回的是Objec的Class實例 )

getGenericInterfaces:獲取接口中的泛型信息。

如(class Chinese extends People<String,Integer,Double> implements SpeakChinese<String>,UseChopsticks<Double>{},返回的是SpeakChinese<String>,UseChopsticks<Double>,若是沒有實現的接口,返回的Type數組長度爲0)。

WildcardType

public interface WildcardType extends Type{
    Type[]    getLowerBounds();  // 獲取下限
    Type[]    getUpperBounds();  // 獲取上限
}

 

測試代碼

List<? extends String> upperBoundsList;
List<? super Integer> lowerBoundsList;

Field upperBoundsList = TypeTest.class.getDeclaredField("upperBoundsList");
ParameterizedType pt = (ParameterizedType)upperBoundsList.getGenericType();
Type[] types = pt.getActualTypeArguments();
System.out.println( ((WildcardType)types[0]).getUpperBounds()[0] ); // class java.lang.String

Field lowerBoundsList = TypeTest.class.getDeclaredField("lowerBoundsList");
ParameterizedType pt1 = (ParameterizedType)lowerBoundsList.getGenericType();
Type[] types1 = pt1.getActualTypeArguments();
System.out.println( ((WildcardType)types1[0]).getLowerBounds()[0] ); //class java.lang.Integer

3.關於泛型擦除

因爲編譯期間存在泛型擦除,因此字節碼對象不會由於泛型而出現差別。泛型擦除存在限制,並非全部的泛型都會擦除,而是隻有函數內建立的局部變量的泛型會被擦除。

3.1 例子1

List<String> ls = new ArrayList<String>();
System.out.println(ls.getClass().equals(ArrayList.class));  // true

 

3.2 例子2

List<String> list = new ArrayList<>();
list.add("string");
// list.add(true); // 編譯報錯
System.out.println(list);

// 使用反射繞過泛型的限制,往限制爲String元素的list中添加其餘類型的元素
Method mt = list.getClass().getDeclaredMethod("add", Object.class);
mt.invoke(list, true);
System.out.println(list);

 

3.3父類的泛型信息不會被擦除

ps:當前類不是泛型類,可是父類是泛型類,這時候父類的泛型必需要被明確的類型指定。除非當前類爲泛型類,父類的泛型沿用當前類的泛型。

以hashMap爲例,當前爲泛型類,並且父類的泛型沿用了當前類的泛型

public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable 

 

獲取泛型信息:

Map<String, Integer> map = new HashMap<String, Integer>();
Type type = map.getClass().getGenericSuperclass();
ParameterizedType parameterizedType = ParameterizedType.class.cast(type);
for (Type typeArgument : parameterizedType.getActualTypeArguments()) {
    System.out.println(typeArgument.getTypeName());
}

 

輸出

K
V

可是若是把

Map<String, Integer> map = new HashMap<String, Integer>();

 

換成

Map<String, Integer> map = new HashMap<String, Integer>(){};

 

以上代碼就會輸出:

java.lang.String
java.lang.Integer

這是由於第一種寫法中,父類沿用子類的泛型,而子類的泛型被擦除了,因此獲取父類的泛型就獲取不到了。第二種中,至關於建立了一個HashMap的匿名內部類對象,父類的泛型信息被保存了下來。

3.4 成員變量的泛型不會被擦除

static Map<String, Integer> map = new HashMap<String, Integer>();

public static void main(String[] args) throws Exception {
    Type type = DemoApplication.class.getDeclaredField("map").getGenericType();
    ParameterizedType parameterizedType = ParameterizedType.class.cast(type);
    for (Type typeArgument : parameterizedType.getActualTypeArguments()) {
        System.out.println(typeArgument.getTypeName());
    }
}

 

運行結果和上一個例子同樣。

3.5 成員函數中的參數泛型不會被擦除

例子2.2中已經顯示出來。

相關文章
相關標籤/搜索