假设有个泛型接口或类,方法上的参数是一个泛型,或者返回值是泛型。然后子类实现了泛型接口,或继承了泛型类。但是子类指定了具体的泛型类型。在子类没有重写父类方法的情况下,我们通过Method
对象是无法反射拿到具体的方法参数或者返回值的,那么如何才能获取到呢?.
示例:
//泛型接口
public interface InterfaceOne<T, O> {
String addStu(O add, T info);
T updateStu(T info);
T getStuInfo(String userId);
}
public interface InterfaceTwo extends InterfaceOne<Student, English> {
}
通过反射拿到InterfaceTwo
接口方法参数类型和返回值类型
public class ReflectTest {
//拼接 method 信息
private String methodStr(Class<?> returnType, Method method, Class<?>[] parameterTypes) {
StringBuilder builder = new StringBuilder();
builder.append(returnType.getSimpleName()).append(" ").append(method.getName()).append("( ");
for (int i = 0; i < parameterTypes.length; i++) {
if (i != 0) {
builder.append(", ");
}
builder.append(parameterTypes[i].getSimpleName());
}
builder.append(" )");
return builder.toString();
}
@Test
public void test2() {
Method[] methods = InterfaceTwo.class.getMethods();
for (Method method : methods) {
System.out.println(methodStr(method.getReturnType(), method, method.getParameterTypes()));
}
}
}
输出效果如下: 可以看到从子类接口InterfaceTwo
拿到的泛型参数是Object 返回值也是Object,但是实际上这个InterfaceTwo已经指定了具体的泛型类型应该拿到Student和English类型才是正确的.
那么是否可以拿到具体的泛型类型呢?答案一定是可以的
如果你使用了Spring框架,那么很庆幸Spring提供一个优秀的反射工具类org.springframework.core.ResolvableType
可以帮助解决这个问题.
Spring ResolvableType示例如下:
@Test
public void test1() {
//Method[] methods = InterfaceOne.class.getMethods();
Method[] methods = InterfaceTwo.class.getMethods();
for (Method method : methods) {
int parameterCount = method.getParameterCount();
Class<?>[] paramClass = new Class<?>[parameterCount];
for (int i = 0; i < parameterCount; i++) {
//获取参数类型,需要指定子类具体是哪个类
Class<?> aClass = ResolvableType.forMethodParameter(method, i, InterfaceTwo.class).toClass();
paramClass[i] = aClass;
}
//获取返回值类型,需要指定子类具体是哪个类,因为InterfaceTwo本身就是一个子类,指定自己即可
Class<?> returnType = ResolvableType.forMethodReturnType(method, InterfaceTwo.class).toClass();
System.out.println(methodStr(returnType, method, paramClass));
}
}
那么如果没用到Spring,需要自己写一个ResolvableType这样的工具类如何写呢?
从开始的示例中其实可以看出,实际上接口InterfaceOne
上泛型T
对应的就是InterfaceTwo
中的Student
,O
对应English
,它们之间是一一对应的。
在子类中我们可以通过如下两个方法拿到继承或实现的泛型类具体类型
//可以实现多个接口,所以返回数组,
Type[] genericInterfaces = InterfaceTwo.class.getGenericInterfaces();
//只能继承一个类,所以返回单个,
ParameterizedType genericSuperclass = (ParameterizedType)InterfaceTwo.class.getGenericSuperclass();
Type
的对应实现类是sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl
, 实现了java.lang.reflect.ParameterizedType
,ParameterizedType
继承Type
,ParameterizedType
保存了子类中具体的泛型类型和父类的泛型定义(被包装成TypeVariableImpl
),这样就可以获取到具体类到泛型的一一对应关系。(建议使用接口java.lang.reflect.TypeVariable
)
当获取的方法参数或者返回值是一个泛型类型时(被包装成TypeVariableImpl
),则可以根据映射关系获取到真实的类型参数。
实现代码如下:
import java.lang.reflect.GenericDeclaration;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.HashMap;
/**
* Created by bruce on 2021/8/11 20:55
*/
public class GenericResolvableType {
private final Class<?> resolvableClass;
private final HashMap<Class<?>, ActualType> actualTypeMap = new HashMap<>();
public GenericResolvableType(Class<?> clazz) {
this.resolvableClass = clazz;
actualTypeMap(clazz);
}
public Class<?> getResolvableClass() {
return resolvableClass;
}
private void actualTypeMap(Class<?> clazz) {
Type genericSuperclass = clazz.getGenericSuperclass();
if (genericSuperclass != null && genericSuperclass != Object.class) {
actualTypeResolve(genericSuperclass);
}
Type[] genericInterfaces = clazz.getGenericInterfaces();
for (Type type : genericInterfaces) {
actualTypeResolve(type);
}
}
private void actualTypeResolve(Type type) {
if (type instanceof ParameterizedType) {
ParameterizedType type1 = (ParameterizedType) type;
Type[] actualTypeArguments = type1.getActualTypeArguments();
Class<?> rawType = (Class<?>) type1.getRawType();
TypeVariable<? extends Class<?>>[] typeParameters = rawType.getTypeParameters();
actualTypeMap.put(rawType, new ActualType(actualTypeArguments, typeParameters));
actualTypeMap(rawType);
} else {
actualTypeMap((Class<?>) type);
}
}
private Class<?> getActualType(TypeVariable<?> typeVariable) {
GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration();
//genericDeclaration可能是method
if (!(genericDeclaration instanceof Class)) {
return null;
}
Class<?> genericDeclareClass = (Class<?>) typeVariable.getGenericDeclaration();
ActualType actualType = actualTypeMap.get(genericDeclareClass);
if (actualType != null) {
Type type = actualType.getActualType(typeVariable.getTypeName());
if (type instanceof TypeVariable) {
return getActualType((TypeVariable<?>) type);
}
return (Class<?>) type;
}
return null;
}
public Class<?>[] getMethodParameter(Method method) {
Class<?>[] parameterTypes = method.getParameterTypes();
Class<?>[] paramTypes = new Class<?>[method.getParameterCount()];
Type[] genericParameterTypes = method.getGenericParameterTypes();
for (int i = 0; i < genericParameterTypes.length; i++) {
Type genericParameterType = genericParameterTypes[i];
if (!(genericParameterType instanceof TypeVariable<?>)) {
paramTypes[i] = (Class<?>) genericParameterType;
} else {
Class<?> actualType = getActualType((TypeVariable<?>) genericParameterType);
paramTypes[i] = actualType != null ? actualType : parameterTypes[i];
}
}
return paramTypes;
}
public Class<?> getReturnType(Method method) {
Type genericReturnType = method.getGenericReturnType();
Class<?> actualType = null;
if (genericReturnType instanceof TypeVariable) {
actualType = getActualType((TypeVariable<?>) genericReturnType);
}
return actualType != null ? actualType : method.getReturnType();
}
static class ActualType {
Type[] actualTypes;
TypeVariable<?>[] typeVariables;
public ActualType(Type[] actualTypes, TypeVariable<?>[] typeVariables) {
this.actualTypes = actualTypes;
this.typeVariables = typeVariables;
}
public Type getActualType(String genericType) {
for (int i = 0; i < typeVariables.length; i++) {
if (typeVariables[i].getName().equals(genericType)) {
return actualTypes[i];
}
}
return null;
}
}
}
测试示例
public interface Interface1<T> {
String iface1Method1(T info);
T iface1Method2(T info);
T iface1Method3(String userId);
}
public interface Interface2<T> {
String iface2Method1(T param);
}
public interface Interface3<T, O> {
String iface3Method1(O add, T info);
T iface3Method2(T info);
T iface3Method3(String userId);
}
public abstract class AbsImpl1<F> implements Interface3<Student, F> {
public abstract F absImpl1Method1(String param);
}
public abstract class AbsImpl2 extends AbsImpl1<Apple> implements Interface2<Student>, Interface1<Long> {
public <T> T study(T course) {
return course;
}
}
@Test
public void test1() {
GenericResolvableType genericResolvableType = new GenericResolvableType(AbsImpl2.class);
Method[] methods1 = AbsImpl2.class.getMethods();
for (Method method : methods1) {
if (method.isBridge() || method.getDeclaringClass() == Object.class) {
continue;
}
Class<?> returnType = genericResolvableType.getReturnType(method);
Class<?>[] methodParameters = genericResolvableType.getMethodParameter(method);
System.out.println(methodStr(returnType,method,methodParameters));
}
}
运行结果: 因为study这个方法确实没有指定泛型类型,所以默认是Object类型