java获取子类继承的泛型接口方法上具体的参数类型

假设有个泛型接口或类,方法上的参数是一个泛型,或者返回值是泛型。然后子类实现了泛型接口,或继承了泛型类。但是子类指定了具体的泛型类型。在子类没有重写父类方法的情况下,我们通过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中的StudentO对应English,它们之间是一一对应的。
在子类中我们可以通过如下两个方法拿到继承或实现的泛型类具体类型

//可以实现多个接口,所以返回数组,
Type[] genericInterfaces = InterfaceTwo.class.getGenericInterfaces();

//只能继承一个类,所以返回单个,
ParameterizedType genericSuperclass = (ParameterizedType)InterfaceTwo.class.getGenericSuperclass();

Type的对应实现类是sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl, 实现了java.lang.reflect.ParameterizedTypeParameterizedType 继承TypeParameterizedType保存了子类中具体的泛型类型父类的泛型定义(被包装成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类型
在这里插入图片描述

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值