JAVA types (Type), generics and type erasure

Table of Contents

  • JAVA type (Type) system
    • 1. History of the Type System
    • 2. Custom tool method
    • 3. Various Types
      • Parameterized type: ParameterizedType
        • getRawType(): Type
        • getOwnerType(): Type
        • getActualTypeArguments(): Type[ ]
      • GenericArrayType: Generic array type
      • TypeVariable: type variable
      • WildcardType: wildcard type
      • new method for new type
  • Generics and type erasure
    • There are children (Class) first, and then there are parents (Type)
    • 1. New methods related to generics
      • Method’s getGenericParameterTypes method
      • Method’s getGenericReturnType method
      • Class getGenericSuperclass method

JAVA type (Type) system

1. History of Type System

reminder
In fact, there is Class first, and then Type. In other words, Java did not have such a concept as a "type system" at the beginning.
It is because after the introduction of the concept of generics, in order to introduce the concept of generics into Java and make backward compatibility,
Thus, a Type ancestor and other brothers are "complemented" for Class, thus perfecting the whole system.

In early Java (before 5.0) all types had a Class object, including basic types and custom types:

Student.class
Teacher.class
String. class
Integer.class
Double.class
Boolean.class
int. class
int[].class
double.class
double[].class
boolean.class
boolean[].class
...

The Class object contains the definition information of the current type, which is the basis of Java reflection. Through a type of Class object, you can query which fields (Field), which methods (Method), which constructors (Constructor) and other information of this type.

However, after JDK 5.0 introduced the concept of generics, Java officials found that some types related to the newly introduced generics do not apply to the sentence we said above that “all types have a Class object”.

“That object” of these generic-related types cannot be classified under the concept of Class object. Their “that object” is both similar to and different from Class objects.

In this regard, Java officially extracted the similarities between them and Class, extracted the concept of Type, and completed other types:

Type
├── Class
├── ParameterizedType
├── TypeVariable
├── WildcardType
└── GenericArrayType

Type and its sub-interfaces and implementation classes (Class, ParameterizedType, TypeVariable, WildcardType, GenericArrayType) together form the Java type system.
Now:
All types that have nothing to do with generics have an object of Class type corresponding to them. For example, String type, String[] type.
All types that are related to generics, according to their specific circumstances, have an object of a subtype of the Type type corresponding to them. For example, List type, T[] type.
Here’s a more typical example:

The type object of type String[] is Class;
A type object of type List is a subtype ParameterizedType of type Type;
A type object of type List[] is a subtype GenericArrayType of type Type.

2. Custom tool method

In order to detect the specific type of Type more conveniently, we can prepare a simple method as follows:

public static String getTypeName(Type type) {<!-- -->
    if (type instanceof Class)
        return "Class"; // just like "String"
    else if (type instance of TypeVariable)
        return "TypeVariable"; // just like "T"
    else if (type instance of ParameterizedType)
        return "ParameterizedType"; // just like "List<String>";
    else if (type instance of GenericArrayType)
        return "GenericArrayType"; // just like "T[]";
    else
        return "something wrong"; // theoretically shouldn't be
}

3. Various types

public class Student<T extends Number> {<!-- -->
    public List<T> a = new ArrayList<>();
    public T b;
    public List<String>[] c;
    public T[] d;
}

Parameterized type: ParameterizedType

Parameterized types are what we usually call generic types. When it comes to parameters, the most familiar thing is that there are formal parameters when defining a method, and then pass the actual parameters when calling this method.

So how to understand the parameterized type? As the name implies, it is to parameterize the type from the original specific type, similar to the variable parameters in the method. At this time, the type is also defined as a parameter form (which can be called a type parameter), and then the specific type is passed in when using/calling type (type argument).

public static void demoA() {<!-- -->
    Field aField = null;
    Field[] fields = Student. class. getDeclaredFields();
    for (Field field : fields) {<!-- -->
        if (field.getName().equals("a")) {<!-- -->
            aField = field;
            break;
        }
    }
  
    ParameterizedType aType = castToParameterizedType(aField. getGenericType());
    System.out.println("rawType: " + aType.getRawType());
    System.out.println(" ownerType: " + aType.getOwnerType());
    for (Type actualTypeArgument : aType. getActualTypeArguments()) {<!-- -->
        System.out.println("ActualTypeArgument: " + actualTypeArgument);
    }
}

ParameterizedType has 3 common and important methods:

getRawType(): Type

The function of this method is to return the type of the current ParameterizedType. As in a List, what is returned is the Type of the List, that is, the type that returns the remainder of the parameter part without reflection.

If you “seeked” the type at the beginning, you did not call the .getGenericType() method, but the .getType() method, .getType() The type returned by the code> method is this RawType.

getOwnerType(): Type

Some generic classes are inner classes, the most typical being Map.Entry .

The .getOwnerType() method of the ParameterizedType type returns the type of the outer class where the generic object of the inner class type is located (a mouthful…)

If there is a public Map.Entry x; attribute in Student, call .getOwnerType() method on it, and the result is Map.

If the generic class is a normal, ordinary class, not an inner class, this method will return null .

getActualTypeArguments(): Type[ ]

The method returns the actual parameter type in the parameterized type <>.

If there is a public Map x in Student; this ParameterizedType returns an Array of Type objects with fully qualified class names of the String and Date classes.

Note: This method only returns the type in the outermost <>, no matter how many <>s are inside the <>.

In addition, if the type of attribute x is not a definite generic type, but uses generic variables like public List x, then pass getActualTypeArguments() method, you get the generic variable T.

GenericArrayType: Generic array type

If there is a property such as public T[ ] x or public List[ ] x in the Student class, the type of these properties is GenericArrayType .

The key method of GenericArrayType is the .getGenericComponentType() method. It returns the type of the members of this generic array.

In the example above, that’s T and List .

Note: No matter how many [] are juxtaposed from left to right, this method only removes the rightmost [] and the remaining content is used as the return value of this method.

Here we know that, theoretically, the type of its constituent elements must be ParameterizedType or TypeVariable.

TypeVariable: type variable

If there is an attribute like public T x in the Student class, the type of the attribute type is TypeVariable.

In addition, as we said earlier, for the attribute public T[] x, its type type is GenericArrayType type, and the type type of its constituent elements is also TypeVariable.

The generic type information will be converted to a specific type at compile time, and TypeVariable is used to reflect the information before the JVM compiles the generic type.

getBounds(): Type[]
Returns the upper bound of the current type, or Object if no upper bound is specified.

getName(): String
Returns the class name of the current type

getGenericDeclaration(): D
Returns the Type of the class the current type is in.

WildcardType: wildcard type

Represents wildcard types, such as , , etc.
getLowerBounds(): Type[] Get the array of lower bounds
getUpperBounds(): Type[] Get the type array of the upper boundary
Note: If no upper bound is specified, it defaults to Object, and if no lower bound is specified, it defaults to String.

New methods for new types

getActualTypeArguments for ParameterizedType
The getGenericSuperclass method of Class returns the generic superclass whose type is Type .

In fact, through the instanceof operator, we can judge that the actual information of the generic parent class is the ParameterizedType type.

The ParameterizedType type has a getActualTypeArguments method that returns the generic parameters used by the generic parent class.

ParameterizedType superclass = (ParameterizedType)StringLinkedList.class.getGenericSuperclass();
for (Type cur : superclass. getActualTypeArguments()) {<!-- -->
    System.out.println(cur); // only one in this example: String
}

Generic types and type erasure

Type is the common parent interface for all types in the Java language. This is the most official explanation.
Class is a direct implementation class of Type. Type and Class, and other subinterfaces (and implementation classes) of Type make up Java’s type system.

There is a child (Class) first, and then a parent (Type)

The Class object contains the definition information of the current type, which is the basis of Java reflection. Through the Class object of a type, you can query information such as which fields, methods, and constructors this type has.
At this time, the Class object of a class “contains” enough relevant information about this class.
For example, for the following class:

public class T1 {<!-- -->
    public int f1;
    public int[] f2;
}

You can use the reflection method to get information about the two properties of f1 and f2:

T1 x = new T1();
System.out.println(x.getClass().getField("f1"));
System.out.println(x.getClass().getField("f2"));

However, after the emergence of the concept of generics, the situation becomes more complicated. For data types involving generics, some original methods (methods that appeared before 1.5) cannot return information in ” Include “generic related information.

For example, if our T1 class from the example above became a generic class:

public class T1<E> {<!-- -->
    public int f1;
    public int[] f2;
  public E[] f3;
  public Object[] f4;
}

Still use the method above.

1. New methods related to generics

Method’s getGenericParameterTypes method

The getGenericParameterTypes method is an upgraded version of the getParameterTypes method.

The function of Method’s getGenericParameterTypes method is similar to the above getParameterTypes method, but because it is a “new” method, its return result will retain the generic information.

Still the same method as above:

public static <T> void demo(T arg0, T[] arg1, String arg2) {<!-- -->
    ...
}

The return value of the getGenericParameterTypes method is an array of Types (not an array of Classes).

Its three elements are

  • The first parameter is of type T , of type TypeVariable ;
  • The second parameter type is T [ ], the type is GenericArrayType ;
  • The third parameter is of type String, of type Class .

Method’s getGenericReturnType method

The getGenericReturnType method is an upgraded version of the getReturnType method

For the same method, Method’s getGenericReturnType method can identify the generic information of the return value type.

Its return is a T whose type is TypeVariable .

Class getGenericSuperclass method

The getGenericSuperclass function of Class is the same as getSuperclass, but it will retain the generic information of the parent class.

StringLinkedList.class.getGenericSuperclass() // LinkedList<String>