Explanation of several sub interface classes of Type

Posted by speedyslow on Sat, 25 Dec 2021 04:09:42 +0100

Type

Type is widely used in reflection and is mainly used to obtain types. For example: whether it is a generic type, whether it is a variable type, and so on.

Get more information through its subclasses. It has five subclasses: Class, GenericArrayType, ParameterizedType, TypeVariable and WildcardType.

Among them, only Class is an entity Class and others are interface classes. This chapter mainly describes interface classes.

ParameterizedType

ParameterizedType is a parameterized type, that is, a type with generic types, such as list < string >, set < long >, map < string, long >, class < float >.

There are three methods, Teyp[] getActualTypeArguments(), Type getRawType(), Type getOwnerType().

  • getActualTypeArguments()

    This method is to obtain the generic Type of this Type. For example, the String Type obtained from the list < String > Type is the String Type. Because there can be multiple generic types, such as map < String, long >, it returns Type [].

    public class TypeTest {
        List<String> ptFiled;
    }
    

    Define a TypeTest class with a generic member variable ptfile.

    Field field = TypeTest.class.getDeclaredField("ptFiled");
    Type type = field.getGenericType();
    if (type instanceof ParameterizedType) {
        Log.e(TAG, "type is ParameterizedType");
        Type[] typeArguments = ((ParameterizedType) type).getActualTypeArguments();
        Log.e(TAG, "type = " + type);
        for (Type item : typeArguments) {
            Log.e(TAG, "actualTypeArgument = " + item);
        }
    }
    

    Get the type of the variable by reflection, determine whether it is ParameterizedType, and then call the getActualTypeArguments() method to get the generic type array and print generic type.

    Print results

    Through getActualTypeArguments(), you can know that the type of generic type is String. If map < String, Long >, you will get String and Long types.

  • getRawType()

    This method is to obtain the corresponding original type. Take the above code as an example. If the variable ptfile D is of type List < string >, then the original type of ptfile D is List.

    if (type instanceof ParameterizedType) {
        Log.e(TAG, "rawType = " + ((ParameterizedType) type).getRawType());
    }
    

    Print results

  • getOwnerType()

    This method is to obtain the owner's type. To put it simply, when the type is an internal class, it obtains the type of the outer class, not the internal class. All the returned values are null.

    public class TypeTest<T extends List & Collection> {
        TypeTestInner<String> ptFiled;
      	List<T> listPtFiled;
    
        static class TypeTestInner<F> {
            List<Integer> list;
        }
    }
    

    The above two properties are ParameterizedType types. When listptfile calls getOwnerType(), it gets null, while ptfile calls * * getOwnerType() * * it gets TypeTest type.

    if (type instanceof ParameterizedType) {
        Log.e(TAG, "type is ParameterizedType");
        Log.e(TAG, "ownerType = " + ((ParameterizedType) type).getOwnerType());
    }
    

    Print result of listptfile call:

    Print result of ptfile call:

TypeVariable

Typevariables are generic types, such as T and E in list < E >.

The interface also has three methods: Type[] getBounds(), D getGenericDeclaration(), and String getName().

  • getBounds()

    This method is to obtain the boundary array. For example, the upper boundary of T in typetest < T extensions List > is List. If there is no upper boundary, the upper boundary is Object.
    Because it is to obtain arrays, there is such a form as typetest < T extensions list & collision >, where the returned Type [] is [List, Collection]. Look at the code more intuitively.

    public class TypeTest<T extends List & Collection> {
        List<T> ptFiled;
        T tvFiled;
    }
    

    Let's take a look at calling and printing, which also uses reflection to obtain the field tvfile.

    if (type instanceof TypeVariable) {
        Log.e(TAG, "type is TypeVariable");
        Type[] bounds = ((TypeVariable) type).getBounds();
        Log.e(TAG, "type = " + type);
        for (Type item : bounds) {
            Log.e(TAG, "bounds = " + item);
        }
    }
    

    Print results:

    List < T > ptfile belongs to ParameterizedType, but t in it is TypeVariable, which can be obtained as follows:

Field field = TypeTest.class.getDeclaredField("ptFiled");
Type type = field.getGenericType();
if (type instanceof ParameterizedType) {
    Log.e(TAG, "type is ParameterizedType");
    Type[] typeArguments = ((ParameterizedType) type).getActualTypeArguments();
    Log.e(TAG, "type = " + type);
    for (Type item : typeArguments) {
        Log.e(TAG, "actualTypeArgument = " + item);
        if (item instanceof TypeVariable) {
            Log.e(TAG, "item is TypeVariable");
            Type[] itemTypes = ((TypeVariable) item).getBounds();
            Log.e(TAG, "item = " + item);
            for (Type itemType : itemTypes) {
                Log.e(TAG, "bounds = " + itemType);
            }
        }
    }
    Log.e(TAG, "rawType = " + ((ParameterizedType) type).getRawType());
}

Print results:

  • getGenericDeclaration()

    This method is to obtain the class genus, that is, to obtain which class it belongs to. For example, the above t tvfile belongs to TypeTest, so the returned type is TypeTest.

    Log.e(TAG, "getGenericDeclaration = " + ((TypeVariable) type).getGenericDeclaration());
    

    Print results:

  • getName()

    This method is to get the generic name, that is, T is to get T.

    Log.e(TAG, "getName = " + ((TypeVariable) type).getName());
    

    Print results:

GenericArrayType

GenericArrayType is a generic array type. This is actually relatively simple to understand. It consists of two preconditions: one is generic, and the other is array, that is, array type with generic.

For example, T[] t, list < T > [] list, etc. have generic types and array types.

It has a method, Type getGenericComponentType().

  • getGenericComponentType()

    This method is to obtain the Type of array, that is, the returned Type is the Type of array, such as T[] t, which returns t, and list < T > List returns list.

    public class TypeTest<T extends List & Collection> {
        List<T> ptFiled;
        T tvFiled;
        T[] gatFiled;
    }
    

    Here, you can get the type of gatfile field or use the above reflection.

    if (type instanceof GenericArrayType) {
        Log.e(TAG, "getGenericComponentType = " + ((GenericArrayType) type).getGenericComponentType());
    }
    

    Print results:

    Is this a little familiar? Yes, this T type is TypeVariable, so you can get the information of this generic type. In the case of list < T > [] list, the obtained type is list < T >, that is, parametrizedtype,

WildcardType

WildcardType is a wildcard type, and the wildcard is * *?, That is, when the type is ParameterizedType, when the generic type is wildcard? The generic type is WildcardType * *.

The interface has two methods, Type[] getUpperBounds(), and Type[] getLowerBounds().

  • getUpperBounds()

    This method is to obtain the upper boundary array, that is, list <? The upper boundary obtained by extensions Map > is map.

  • getLowerBounds()

    This method is to obtain the lower boundary array, that is, list <? The lower boundary obtained by super Set > is Set.

    public class TypeTest<T extends List & Collection> {
        List<T> ptFiled;    
        T tvFiled;    
        T[] gatFiled;    
        List<? extends Map> upperWtFiled;    
        List<? super Set> lowerWtFiled;    
        List<?> wfFiled;
    }
    

    Here, try to get the lowerWtFiled field.

    for (Field field : TypeTest.class.getDeclaredFields()) {
        Type type = field.getGenericType();    
        if (type instanceof ParameterizedType) {
            Log.e(TAG, field.getName() + " is ParameterizedType");        
            Type[] typeArguments = ((ParameterizedType) type).getActualTypeArguments();        
            for (Type item : typeArguments) {
                if (item instanceof WildcardType) {                
                    Log.e(TAG, field.getName() + " item is WildcardType");                
                    Log.e(TAG, "upper = " + Arrays.toString(((WildcardType) item).getUpperBounds()));                
                    Log.e(TAG, "lower = " + Arrays.toString(((WildcardType) item).getLowerBounds()));            
                }        
            }    
        }
    }
    

    Print results:

    There is no upper boundary for lowerWtFiled and wffile. The default is Object, while the lower boundary of lowerWtFiled is Set.

ending

There are only four Type series interfaces, and there are not many methods, but they are very useful. Most of them get information around generics.

Here is a useful example: for example, the two Factory classes in Retrofit also rely on Type to convert data and return the defined format, and can also be used together through annotation.

The next article will talk about the two Factory classes of Retrofit.

Topics: Java Android reflection