Generally, we use Class to describe data types. In fact, there is a more general type interface type in JDK. Type is the common parent interface of all types in JDK, and Class is also one of the implementation classes of type.
public interface Type { default String getTypeName() { return this.toString(); } }
Before there were no generics, Java had only raw type s. At this time, all types were described by Class.
public final class Class<T> implements Serializable, GenericDeclaration, Type, AnnotatedElement
After adding generics, the JDK extends types and adds the following four types outside Class.
type | describe |
---|---|
ParameterizedType | Parameterized types, that is, generic types, such as list < string >, map < integer, string > |
GenericArrayType | Generic array type, for example: T [] |
TypeVariable | Type variable type, for example: T in list < T > |
WildcardType | Wildcard types are not JAVA types, but generic expressions, such as:? super T,? extends T |
Usage of ParameterizedType
ParameterizedType represents a parameterized type. The so-called parameter refers to the generic type in < >.
public interface ParameterizedType extends Type { // Get generic types within < > Type[] getActualTypeArguments(); // Gets the original type, and returns O if the generic structure is O < T > Type getRawType(); // If the generic structure is o < T >. I < s >, return the o < T > of the outer layer Type getOwnerType(); }
Gets the generic information of the parent class
There is a getGenericSuperclass() method in the Class class, which is used to obtain the parent Class with generic information. If the parent Class does not have generic information, it is equivalent to the getSuperclass() method.
Create a parent class without generics and implement
public class GenericService { } public class UserService extends GenericService { }
public class GenericTest { public static void main(String[] args) { Type genericSuperClass = UserService.class.getGenericSuperclass(); Class<? super UserService> superclass = UserService.class.getSuperclass(); System.out.println(genericSuperClass); System.out.println(superclass); System.out.println(genericSuperClass == superclass); } }
The output is as follows
class test.GenericService class test.GenericService true
You can see that both getGenericSuperclass and getSuperclass return the same Class.
Next, use generics to verify again. Here you need two classes UserRepository and User, which can be created by yourself.
public class GenericService<T, M> { } public class UserService extends GenericService<UserRepository, User> { }
Running again, you can see that the objects returned by the two methods are different. The getGenericSuperclass method returns the ParameterizedType with generic information.
test.GenericService<test.UserRepository, test.User> class test.GenericService false
Generics can be further obtained through ParameterizedType
public class GenericTest { public static void main(String[] args) { Type genericSuperClass = UserService.class.getGenericSuperclass(); ParameterizedType parameterizedType = (ParameterizedType) genericSuperClass; // Get original type Class displayType(parameterizedType.getRawType()); // Get generics Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); for (Type argument : actualTypeArguments) { displayType(argument); } } public static void displayType(Type type) { System.out.println(type.getTypeName() + " --- " + type.getClass().getSimpleName()); } }
Output results
test.GenericService --- Class test.UserRepository --- Class test.User --- Class
Gets the generic information of the interface
A getGenericInterfaces() method is also provided in the Class class to obtain the interface with generic information.
Create two interfaces, one without generics and one with generics
public interface IA { } public interface IB<T, P extends Serializable> { }
Create an implementation class
public class Impl implements IA, IB<UserRepository, User> { }
Gets the generic type of the interface
public class GenericTest { public static void main(String[] args) { Type[] genericInterfaces = Impl.class.getGenericInterfaces(); for (Type type : genericInterfaces) { if (type instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) type; displayType(parameterizedType); Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); for (Type argument : actualTypeArguments) { displayType(argument); } } else { displayType(type); } System.out.println("-------------------------------"); } } public static void displayType(Type type) { System.out.println(type.getTypeName() + " --- " + type.getClass().getSimpleName()); } }
Output results
test.IA --- Class ------------------------------- test.IB<test.UserRepository, test.User> --- ParameterizedTypeImpl test.UserRepository --- Class test.User --- Class -------------------------------
Provide a tool class to get the parent class and interface generics
public class ReflectUtils { /** * Gets the generic type at the specified location of the parent class */ public static Class<?> getSuperClassGenericType(Class<?> clazz, int index) { Type genType = clazz.getGenericSuperclass(); if (!(genType instanceof ParameterizedType)) { log.warn(String.format("Warn: %s's superclass not ParameterizedType", clazz.getSimpleName())); return Object.class; } return indexOfGenericType(clazz, (ParameterizedType) genType, index); } /** * Gets the generic type of the specified interface at the specified location */ public static Class<?> getInterfaceGenericType(Class<?> clazz, Class<?> target, int index) { for (Type genericInterface : clazz.getGenericInterfaces()) { if (genericInterface instanceof ParameterizedType) { if (((ParameterizedType) genericInterface).getRawType() == target) { return indexOfGenericType(clazz, (ParameterizedType) genericInterface, index); } } else if (genericInterface == target) { log.warn(String.format("Warn: %s's interface not ParameterizedType", clazz.getSimpleName())); return Object.class; } } return Object.class; } public static Class<?> indexOfGenericType(Class<?> clazz, ParameterizedType type, int index) { Type[] params = type.getActualTypeArguments(); if (index >= params.length || index < 0) { log.warn(String.format("Warn: Index: %s, Size of %s's Parameterized Type: %s .", index, clazz.getSimpleName(), params.length)); return Object.class; } if (!(params[index] instanceof Class)) { log.warn(String.format("Warn: %s not set the actual class on superclass generic parameter", clazz.getSimpleName())); return Object.class; } return (Class<?>) params[index]; } }
Gets the generic type of the field
A getGenericType() method is provided in the Field class to obtain the Field type with generic information. If there is no generic information, this method is equivalent to getType().
public class GenericTest { private List<String> list; private List unknownList; private Map<String, Long> map; private Map unknownMap; private Map.Entry<String, Long> entry; public static void main(String[] args) { Field[] fields = GenericTest.class.getDeclaredFields(); for (Field f : fields) { System.out.println(f.getName() + " is ParameterizedType: " + (f.getGenericType() instanceof ParameterizedType)); } } }
The output is as follows. You can see whether there is a generic type, not whether there is a generic type in the Class declaration, but whether there is a generic type in the variable declaration. For example, if unknownList does not declare a generic type, the getGenericType() method returns a Class instead of ParameterizedType.
list is ParameterizedType: true unknownList is ParameterizedType: false map is ParameterizedType: true unknownMap is ParameterizedType: false entry is ParameterizedType: true
Get generic information for generic variables
public class GenericTest { private List<String> list; private List unknownList; private Map<String, Long> map; private Map unknownMap; private Map.Entry<String, Long> entry; public static void main(String[] args) { Field[] fields = GenericTest.class.getDeclaredFields(); for (Field f : fields) { if (f.getGenericType() instanceof ParameterizedType) { System.out.print(f.getName() + "<"); ParameterizedType genericType = (ParameterizedType) f.getGenericType(); Type[] typeArguments = genericType.getActualTypeArguments(); int i = 0; for (Type argument : typeArguments) { if (i++ > 0) { System.out.print(","); } System.out.print(argument.getTypeName()); } System.out.println(">"); } } } }
The output is as follows
list<java.lang.String> map<java.lang.String,java.lang.Long> entry<java.lang.String,java.lang.Long>
Here you can also take a look at the usage of the getOwnerType() method
public class GenericTest { private List<String> list; private List unknownList; private Map<String, Long> map; private Map unknownMap; private Map.Entry<String, Long> entry; public static void main(String[] args) { Field[] fields = GenericTest.class.getDeclaredFields(); for (Field f : fields) { if (f.getGenericType() instanceof ParameterizedType) { ParameterizedType genericType = (ParameterizedType) f.getGenericType(); System.out.println(f.getName() + " ownerType is " + (genericType.getOwnerType() == null ? "null" : genericType.getOwnerType().getTypeName())); } } } }
The output is as follows. You can see that when the generic structure is o < T >. I < s > type, calling getOwnerType() will return o < T > of the outer layer, otherwise it will return null.
list ownerType is null map ownerType is null entry ownerType is java.util.Map
Gets generic information for the method
public class GenericTest { public static void main(String[] args) { Method[] methods = GenericTest.class.getDeclaredMethods(); for (Method method : methods) { if (method.getName().equals("test")) { Type[] genericParameterTypes = method.getGenericParameterTypes(); for (Type genericParameterType : genericParameterTypes) { if (genericParameterType instanceof ParameterizedType) { System.out.println(genericParameterType.getTypeName()); } } } } } public <T> T test(List<String> l1, List<ArrayList<String>> l2, List<T> l3, List<? extends Number> l4, List<ArrayList<String>[]> l5, Map<Boolean, Integer> l6) { return null; } }
The output is as follows
java.util.List<java.lang.String> java.util.List<java.util.ArrayList<java.lang.String>> java.util.List<T> java.util.List<? extends java.lang.Number> java.util.List<java.util.ArrayList<java.lang.String>[]> java.util.Map<java.lang.Boolean, java.lang.Integer>
Usage of GenericArrayType
GenericArrayType represents a generic array
public interface GenericArrayType extends Type { Type getGenericComponentType(); }
Create a generic class that contains a generic array
public class Holder<T> { private T[] arrayData; public void test(List<String>[] listArray, T[] values) {} }
public class GenericTest { public static void main(String[] args) { Field[] fields = Holder.class.getDeclaredFields(); for (Field field : fields) { if (field.getGenericType() instanceof GenericArrayType) { GenericArrayType genericType = (GenericArrayType) field.getGenericType(); System.out.println(field.getName() + " is " + genericType.getTypeName() + " and componentType is " + genericType.getGenericComponentType().getTypeName()); } } } }
The output is as follows
arrayData is T[] and componentType is T
Usage of TypeVariable
TypeVariable represents a generic variable. As long as the generic is not a specific type, such as < T >, < e extensions T >, they are classified as generic variables.
public interface TypeVariable<D extends GenericDeclaration> extends Type, AnnotatedElement { // Get variable name String getName(); // Get original type D getGenericDeclaration(); // Gets the inheritance relationship of the generic type Type[] getBounds(); AnnotatedType[] getAnnotatedBounds(); }
Gets the return parameter of the method
public class GenericTest { public static void main(String[] args) { Method[] methods = GenericTest.class.getDeclaredMethods(); Method method = Arrays.stream(methods) .filter(v -> v.getName().equals("testTypeVariable")) .findAny().get(); Type type = method.getGenericReturnType(); displayType(type); TypeVariable typeVariable = (TypeVariable) type; System.out.println(typeVariable.getGenericDeclaration()); displayType(typeVariable.getBounds()[0]); } public static void displayType(Type type) { System.out.println(type.getTypeName() + " --- " + type.getClass().getSimpleName()); } public <T extends User> T testTypeVariable() { return null; } }
The output is as follows
T --- TypeVariableImpl public test.User test.Test.testTypeVariable() test.User --- Class
Usage of WildcardType
WildcardType indicates wildcard, i.e. <? > This interface contains two methods
- getUpperBounds: wildcard format, such as <? Extensions P > this indicates that the generic type inherits from P, and the getUpperBounds method can obtain the type of P
- getLowerBounds: wildcard format, such as <? Super C > this indicates that the generic type is a superclass of C, and the getLowerBounds method can obtain the type of C
public interface WildcardType extends Type { // Upward inheritance Type[] getUpperBounds(); // Downward inheritance Type[] getLowerBounds(); }
Get the parameters of the method and get the wildcard information defined in the parameters
public class GenericTest { public static void main(String[] args) { Method[] methods = GenericTest.class.getDeclaredMethods(); Method method = Arrays.stream(methods) .filter(v -> v.getName().equals("testWildcardType")) .findAny().get(); Type[] types = method.getGenericParameterTypes(); int index = 0; for (Type type : types) { ParameterizedType parameterizedType = (ParameterizedType) type; Type typeArgument = parameterizedType.getActualTypeArguments()[0]; System.out.println("parameter" + ++index + "Generic type:" + typeArgument.getClass().getSimpleName()); if (typeArgument instanceof WildcardType) { WildcardType wildcardType = (WildcardType) typeArgument; for (Type upperType : wildcardType.getUpperBounds()) { System.out.println(" upperType: " + upperType.getTypeName()); } for (Type lowerType : wildcardType.getLowerBounds()) { System.out.println(" lowerType: " + lowerType.getTypeName()); } } } } public <T> void testWildcardType(List<T> l1, List<?> l2, List<? extends T> l3, List<? super Integer> l4) { } }
The output is as follows
Parameter 1 generic type: TypeVariableImpl Parameter 2 generic type: WildcardTypeImpl upperType: java.lang.Object Parameter 3 generic type: WildcardTypeImpl upperType: T Parameter 4 generic type: WildcardTypeImpl upperType: java.lang.Object lowerType: java.lang.Integer