简体   繁体   English

在其抽象超类中使用任何嵌套子类的泛型类型

[英]Using a generic type of any nested subclass within its abstract superclass

Suppose you have the following abstract java class:假设您有以下抽象 java 类:

public abstract class AbstractRequestHandler<I,O> {
    I input;
    O output;
}

and the following child classes hierarchy:以及以下子类层次结构:

public abstract class AbstractUserRequestHandler<I extends User,O> extends AbstractRequestHandler<I,O>{...}
public abstract class AbstractUniversityRequestHandler<I extends UniversityUser> extends AbstractUserRequestHandler<I,String>{...}
public class StudentRequestHandler extends AbstractUniversityRequestHandler<Student>{...}
public class TeacherRequestHandler extends AbstractUniversityRequestHandler<Teacher>{...}

Suppose you need to use at a given point on the super class the generic type, for example in order to deserialize on the constructor the request json to the specific request object using gson library as follow:假设您需要在超类的给定点使用泛型类型,例如为了在构造函数上使用 gson 库将请求 json 反序列化为特定的请求对象,如下所示:

public AbstractRequestHandler(final String inputJson) {
        input = new Gson().fromJson(inputJson,typeOfI);
}

You need the type of generic I within variable "typeOfI"您需要变量“typeOfI”中的泛型 I 类型

Is there a global solution that allows to get the generic type specified by a concrete child class that respects the following constraints?是否有一个全局解决方案允许获取由遵守以下约束的具体子类指定的泛型类型?

  1. The type is gotten at runtime regardless the child classes hierarchy ( that can be also more complex the one given as example on this question )无论子类层次结构如何,都会在运行时获取类型(这也可能更复杂,作为此问题的示例)
  2. The developer just needs to define the generic extending the super class without manually specify the generic type somewhere on concrete child class ( for example on overrided method or constructor )开发人员只需要定义扩展超类的泛型,而无需在具体子类的某处手动指定泛型类型(例如在重写的方法或构造函数上)

So that if you want to define a new concrete child that assign a new value to a generic you can just write the following concrete class for example:因此,如果您想定义一个新的具体子项,为泛型分配一个新值,您只需编写以下具体类,例如:

public class StudentRequestHandler extends AbstractUniversityRequestHandler<Student>{

    public StudentRequestHandler(String inputJson) {
        super(inputJson);
    }

}

I found the following solutions but they don't respect both the asked solution constraints.我找到了以下解决方案,但他们不尊重所要求的解决方案约束。

Solution that breaks constraint n°2打破约束n°2的解决方案

A solution could be to define an abstract method on the superclass as follow一个解决方案可能是在超类上定义一个抽象方法,如下所示

protected abstract Type getRequestType();

and then implement it on every concrete child class that defines the generic:然后在定义泛型的每个具体子类上实现它:

public class StudentRequestHandler extends AbstractUniversityRequestHandler<Student>{

    public StudentRequestHandler(String inputJson) {
        super(inputJson);
    }

    @Override
    protected Type getRequestType() {
        return Student.class;
    }
}

Then the getRequestType() method can be used on constructor on the target superclass:然后可以在目标超类的构造函数上使用getRequestType()方法:

public AbstractRequestHandler(final String inputJson) {
        request = new Gson().fromJson(inputJson,getRequestType());
}

But even if it works regardless the child classes hierarchy ( respect constraint n°1 ) the developer should manually implement an abstract method on each concrete child class.但即使不管子类层次结构(尊重约束 n°1 )都能工作,开发人员也应该在每个具体的子类上手动实现一个抽象方法。

Solution that breaks constraint n°1打破约束n°1的解决方案

If the hierarchy is simple having only a direct child that extend from the target superclass , as for example:如果层次结构很简单,只有一个从目标超类扩展的直接子类,例如:

public class TeacherRequestHandler extends AbstractRequestHandler<Teacher,String>{...}

a working solution has been proposed by @naikus ( https://stackoverflow.com/users/306602/naikus ) on the following stackoverflow thread: Using a generic type of a subclass within it's abstract superclass? @naikus ( https://stackoverflow.com/users/306602/naikus ) 在以下 stackoverflow 线程上提出了一个可行的解决方案: 在其抽象超类中使用子类的泛型类型?

However this doesn't work if the concrete class is not a direct child of the superclass that defines the generics ( as the one proposed as example on this question ).但是,如果具体类不是定义泛型的超类的直接子类(如在此问题上作为示例提出的那样),则这不起作用。

Edit: after reading your answer and testing many other possible cases I decided to edit your code and re-write it to support all other possible edge cases to include tracking of generics nested deeply inside other generic types.编辑:在阅读您的答案并测试了许多其他可能的情况后,我决定编辑您的代码并重新编写它以支持所有其他可能的边缘情况,以包括跟踪嵌套在其他泛型类型中的泛型。

Sadly to support all cases we need a lot more code than you provided, generics are very tricky, like consider class like this:遗憾的是,为了支持所有情况,我们需要比您提供的代码多得多的代码,泛型非常棘手,例如考虑这样的类:

private class SomeClass<A, B, C, D, E, F> {}

private class SomeConfusingClass<A> extends SomeClass<List<Void>[], List<? extends A>[], List<? extends A[][][]>[][][], List<? extends String[]>[], Map<List<? extends A[]>, A[][]>[], A> {}

private class TestClass extends SomeConfusingClass<Void> {}

To even start doing this we need to have own implementation of java generic types to later be able to construct types like List<String>[] as there is no way to create such type dynamically with raw java API.为了开始这样做,我们需要有自己的 Java 泛型类型的实现,以便以后能够构造像List<String>[]这样的类型,因为无法使用原始 Java API 动态创建此类类型。
This is pretty popular way of handling generic in libraries like that, you can see similar thing in jackson library and many more.这是在像这样的库中处理泛型的非常流行的方式,您可以在 jackson 库中看到类似的东西等等。
So we need implementation of GenericArrayType , ParameterizedType and WildcardType :所以我们需要实现GenericArrayTypeParameterizedTypeWildcardType

private static class ResolvedGenericArrayType implements GenericArrayType {
    private final Type genericComponentType;

    ResolvedGenericArrayType(Type genericComponentType) {
        this.genericComponentType = genericComponentType;
    }

    @Override
    public Type getGenericComponentType() {
        return genericComponentType;
    }

    public String toString() {
        return getGenericComponentType().toString() + "[]";
    }

    @Override
    public boolean equals(Object o) {
        if (o instanceof GenericArrayType) {
            GenericArrayType that = (GenericArrayType) o;
            return Objects.equals(genericComponentType, that.getGenericComponentType());
        } else
            return false;
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(genericComponentType);
    }
}

private static class ResolvedParameterizedType implements ParameterizedType {
    private final Type[] actualTypeArguments;
    private final Class<?> rawType;
    private final Type ownerType;

    private ResolvedParameterizedType(Type rawType, Type[] actualTypeArguments, Type ownerType) {
        this.actualTypeArguments = actualTypeArguments;
        this.rawType = (Class<?>) rawType;
        this.ownerType = (ownerType != null) ? ownerType : this.rawType.getDeclaringClass();
    }

    public Type[] getActualTypeArguments() {
        return actualTypeArguments.clone();
    }

    public Class<?> getRawType() {
        return rawType;
    }

    public Type getOwnerType() {
        return ownerType;
    }

    @Override
    public boolean equals(Object o) {
        if (!(o instanceof ParameterizedType)) {
            return false;
        }
        ParameterizedType that = (ParameterizedType) o;
        if (this == that)
            return true;
        Type thatOwner = that.getOwnerType();
        Type thatRawType = that.getRawType();
        return Objects.equals(ownerType, thatOwner) && Objects.equals(rawType, thatRawType) &&
                Arrays.equals(actualTypeArguments, that.getActualTypeArguments());
    }

    @Override
    public int hashCode() {
        return Arrays.hashCode(actualTypeArguments) ^
                Objects.hashCode(ownerType) ^
                Objects.hashCode(rawType);
    }

    public String toString() {
        StringBuilder sb = new StringBuilder();
        if (ownerType != null) {
            sb.append(ownerType.getTypeName());
            sb.append("$");
            if (ownerType instanceof ResolvedParameterizedType) {
                sb.append(rawType.getName().replace(((ResolvedParameterizedType) ownerType).rawType.getName() + "$", ""));
            } else
                sb.append(rawType.getSimpleName());
        } else
            sb.append(rawType.getName());
        if (actualTypeArguments != null) {
            StringJoiner sj = new StringJoiner(", ", "<", ">");
            sj.setEmptyValue("");
            for (Type t : actualTypeArguments) {
                sj.add(t.getTypeName());
            }
            sb.append(sj.toString());
        }
        return sb.toString();
    }
}

private static class ResolvedWildcardType implements WildcardType {
    private final Type[] upperBounds;
    private final Type[] lowerBounds;

    public ResolvedWildcardType(Type[] upperBounds, Type[] lowerBounds) {
        this.upperBounds = upperBounds;
        this.lowerBounds = lowerBounds;
    }

    public Type[] getUpperBounds() {
        return upperBounds.clone();
    }

    public Type[] getLowerBounds() {
        return lowerBounds.clone();
    }

    public String toString() {
        Type[] lowerBounds = getLowerBounds();
        Type[] bounds = lowerBounds;
        StringBuilder sb = new StringBuilder();
        if (lowerBounds.length > 0)
            sb.append("? super ");
        else {
            Type[] upperBounds = getUpperBounds();
            if (upperBounds.length > 0 && !upperBounds[0].equals(Object.class)) {
                bounds = upperBounds;
                sb.append("? extends ");
            } else
                return "?";
        }
        StringJoiner sj = new StringJoiner(" & ");
        for (Type bound : bounds) {
            sj.add(bound.getTypeName());
        }
        sb.append(sj.toString());
        return sb.toString();
    }

    @Override
    public boolean equals(Object o) {
        if (o instanceof WildcardType) {
            WildcardType that = (WildcardType) o;
            return Arrays.equals(this.getLowerBounds(), that.getLowerBounds()) && Arrays.equals(this.getUpperBounds(), that.getUpperBounds());
        } else
            return false;
    }

    @Override
    public int hashCode() {
        Type[] lowerBounds = getLowerBounds();
        Type[] upperBounds = getUpperBounds();
        return Arrays.hashCode(lowerBounds) ^ Arrays.hashCode(upperBounds);
    }
} 

You can basically copy them from JDK and just do some cleanup.您基本上可以从 JDK 复制它们并进行一些清理。

Next utility we need is a function to validate at the end if we did everything right, like we don't want to return Map<List<? extends X>[]>我们需要的下一个实用程序是一个函数,如果我们做的一切正确,最后验证,就像我们不想返回Map<List<? extends X>[]> Map<List<? extends X>[]> where X is still not resolved TypeVariable : Map<List<? extends X>[]>其中X仍未解析TypeVariable

private static boolean isDefined(Type type) {
    if (type instanceof Class) {
        return true;
    }
    if (type instanceof GenericArrayType) {
        return isDefined(((GenericArrayType) type).getGenericComponentType());
    }
    if (type instanceof WildcardType) {
        for (Type lowerBound : ((WildcardType) type).getLowerBounds()) {
            if (!isDefined(lowerBound)) {
                return false;
            }
        }
        for (Type upperBound : ((WildcardType) type).getUpperBounds()) {
            if (!isDefined(upperBound)) {
                return false;
            }
        }
        return true;
    }
    if (!(type instanceof ParameterizedType)) {
        return false;
    }
    for (Type typeArgument : ((ParameterizedType) type).getActualTypeArguments()) {
        if (!isDefined(typeArgument)) {
            return false;
        }
    }
    return true;
}

Simple recursive function will do this for us.简单的递归函数将为我们做到这一点。 We just check for every possible generic type and check if every member of it is also defined, and unless we will find some hidden TypeVariable we are fine.我们只是检查每个可能的泛型类型并检查它的每个成员是否也被定义,除非我们能找到一些隐藏的TypeVariable ,否则我们没问题。
Main function can stay the same as in your code, we only will edit that one check at the end to use our new function: Main 函数可以与您的代码保持一致,我们只会在最后编辑那个检查以使用我们的新函数:

public static Type getParameterizedType(Class<?> klass, Class<?> rootClass, int paramTypeNumber) throws GenericsException {

    int targetClassParametersNumber = rootClass.getTypeParameters().length;
    if (targetClassParametersNumber == 0) {
        throw new GenericsException(String.format("Target class [%s] has no parameters type", rootClass.getName()));
    } else if (targetClassParametersNumber - 1 < paramTypeNumber)
        throw new GenericsException(String.format("Target class [%s] has parameters type which index start from [0] to [%s]. You requested instead parameter with index [%s]", rootClass, paramTypeNumber - 1, targetClassParametersNumber));

    Type type = analyzeParameterizedTypes(klass, klass, rootClass, paramTypeNumber, null);
    if (!isDefined(type))
        throw new GenericsException(String.format("Parameter [%s] with index [%d] defined on class [%s] has not been valued yet on child class [%s]", type, paramTypeNumber, rootClass.getName(), klass.getName()));
    return type;
}

Now lets work on our main现在让我们处理我们的主要工作

public static Type analyzeParameterizedTypes(final Class<?> klass, final Class<?> targetClass, final Class<?> rootClass, final int paramTypeNumber, Map<Integer, Type> childClassTypes) throws GenericsException { 

function, the begging stays the same, we collect all TypeVariable to simple map, keeping already collected information from previous loop on previous class.函数,begging 保持不变,我们将所有TypeVariable收集到简单映射,保留从前一个类上的前一个循环中收集到的信息。

    Type superclassType = klass.getGenericSuperclass();
    Map<TypeVariable<?>, Type> currentClassTypes = new HashMap<>();
    int z = 0;
    if (childClassTypes != null) {
        for (TypeVariable<?> variable : klass.getTypeParameters()) {
            currentClassTypes.put(variable, childClassTypes.get(z));
            z++;
        }
    }

Then we have our loop collecting and refining our type arguments:然后我们让循环收集和改进我们的类型参数:

    Map<Integer, Type> superClassesTypes = new HashMap<>();
    if (superclassType instanceof ParameterizedType) {
        int i = 0;
        for (final Type argType : ((ParameterizedType) superclassType).getActualTypeArguments()) {
            if (argType instanceof TypeVariable) {
                superClassesTypes.put(i, currentClassTypes.containsKey(argType) ? currentClassTypes.get(argType) : argType);
            } else {
                superClassesTypes.put(i, refineType(klass, argType, currentClassTypesByName));
            }
            i++;
        }
    }

There 2 paths for each type argument, if its TypeVariable we just keep tracking it, and if its anything else we try to "refine" it from any possible references to TypeVariable .每个类型参数有 2 个路径,如果它的 TypeVariable 我们只是继续跟踪它,如果它还有其他任何东西,我们尝试从对TypeVariable任何可能引用中“精炼”它。 This is the most complicated process of this code, and this is why we needed all these classes above.这是这段代码中最复杂的过程,这就是为什么我们需要上面所有这些类的原因。
We start from this simple recursive dispatch method that handles all possible types:我们从这个处理所有可能类型的简单递归调度方法开始:

private static Type refineType(Type type, Map<TypeVariable<?>, Type> typeVariablesMap) throws GenericsException {
    if (type instanceof Class) {
        return type;
    }
    if (type instanceof GenericArrayType) {
        return refineArrayType((GenericArrayType) type, typeVariablesMap);
    }
    if (type instanceof ParameterizedType) {
        return refineParameterizedType((ParameterizedType) type, typeVariablesMap);
    }
    if (type instanceof WildcardType) {
        return refineWildcardType((WildcardType) type, typeVariablesMap);
    }
    if (type instanceof TypeVariable) {
        return typeVariablesMap.get(type);
    }
    throw new GenericsException("Unsolvable generic type: " + type);
}

And small utility method to run it on array of types:以及在类型数组上运行它的小实用方法:

private static Type[] refineTypes(Type[] types, Map<TypeVariable<?>, Type> typeVariablesMap) throws GenericsException {
    Type[] refinedTypes = new Type[types.length];
    for (int i = 0; i < types.length; i++) {
        refinedTypes[i] = refineType(types[i], typeVariablesMap);
    }
    return refinedTypes;
}

Each type goes to own function, or if its TypeVariable we just fetch resolved one from map.每个类型都有自己的函数,或者如果它的TypeVariable我们只是从地图中获取一个已解决的。 Note that this can return null, and I did not handle it here.注意这可以返回null,这里我没有处理。 This could be improved later.这可以在以后改进。 For classes we don't need to do anything so we can just return class itself.对于类,我们不需要做任何事情,所以我们可以只返回类本身。

For GenericArrayType we need to first find out how many dimension such array might have (this could be handled by recursion in our refine method too, but then its a bit harder to debug in my opinion):对于GenericArrayType我们需要首先找出这样的数组可能有多少维(这也可以在我们的精炼方法中通过递归处理,但在我看来调试起来有点困难):

private static int getArrayDimensions(GenericArrayType genericArrayType) {
    int levels = 1;
    GenericArrayType currentArrayLevel = genericArrayType;
    while (currentArrayLevel.getGenericComponentType() instanceof GenericArrayType) {
        currentArrayLevel = (GenericArrayType) currentArrayLevel.getGenericComponentType();
        levels += 1;
    }
    return levels;
}

Then we want to extract that nested component type of array, so for List<A>[][][] we want just List<A> :然后我们要提取数组的嵌套组件类型,因此对于List<A>[][][]我们只需要List<A>

private static Type getArrayNestedComponentType(GenericArrayType genericArrayType) {
    GenericArrayType currentArrayLevel = genericArrayType;
    while (currentArrayLevel.getGenericComponentType() instanceof GenericArrayType) {
        currentArrayLevel = (GenericArrayType) currentArrayLevel.getGenericComponentType();
    }
    return currentArrayLevel.getGenericComponentType();
}

And then we need to refine this type, so our List<A> will change to eg List<String> :然后我们需要细化这个类型,所以我们的List<A>将更改为例如List<String>

    Type arrayComponentType = refineType(getArrayNestedComponentType(genericArrayType), typeVariablesMap);

And rebuild our generic structure using refined type, so our created List<String> will change back to List<String>[][][] :并使用精炼类型重建我们的通用结构,因此我们创建的List<String>将变回List<String>[][][]

private static Type buildArrayType(Type componentType, int levels) throws GenericsException {
    if (componentType instanceof Class) {
        return Array.newInstance(((Class<?>) componentType), new int[levels]).getClass();
    } else if (componentType instanceof ParameterizedType) {
        GenericArrayType genericArrayType = new ResolvedGenericArrayType(componentType);
        for (int i = 1; i < levels; i++) {
            genericArrayType = new ResolvedGenericArrayType(genericArrayType);
        }
        return genericArrayType;
    } else {
        throw new GenericsException("Array can't be of generic type");
    }
}

And whole function looks like this:整个函数看起来像这样:

private static Type refineArrayType( GenericArrayType genericArrayType, Map<TypeVariable<?>, Type> typeVariablesMap) throws GenericsException {
    int levels = getArrayDimensions(genericArrayType);
    Type arrayComponentType = refineType(getArrayNestedComponentType(genericArrayType), typeVariablesMap);
    return buildArrayType(arrayComponentType, levels);
}

For ParameterizedType its much simpler, we just refine type arguments, and create new ParameterizedType instance with these refined arguments:对于ParameterizedType更简单,我们只是细化类型参数,并使用这些细化的参数创建新的ParameterizedType实例:

private static Type refineParameterizedType(ParameterizedType parameterizedType, Map<TypeVariable<?>, Type> typeVariablesMap) throws GenericsException {
    Type[] refinedTypeArguments = refineTypes(parameterizedType.getActualTypeArguments(), typeVariablesMap);
    return new ResolvedParameterizedType(parameterizedType.getRawType(), refinedTypeArguments, parameterizedType.getOwnerType());
}

Same for WildcardType : WildcardType相同:

private static Type refineWildcardType(WildcardType wildcardType, Map<TypeVariable<?>, Type> typeVariablesMap) throws GenericsException {
    Type[] refinedUpperBounds = refineTypes(wildcardType.getUpperBounds(), typeVariablesMap);
    Type[] refinedLowerBounds = refineTypes(wildcardType.getLowerBounds(), typeVariablesMap);
    return new ResolvedWildcardType(refinedUpperBounds, refinedLowerBounds);
}

And this leaves us with whole analyze function looking like this:这让我们的整个分析功能看起来像这样:

public static Type analyzeParameterizedTypes(final Class<?> klass, final Class<?> targetClass, final Class<?> rootClass, final int paramTypeNumber, Map<Integer, Type> childClassTypes) throws GenericsException {
    Type superclassType = klass.getGenericSuperclass();
    Map<TypeVariable<?>, Type> currentClassTypes = new HashMap<>();
    int z = 0;
    if (childClassTypes != null) {
        for (TypeVariable<?> variable : klass.getTypeParameters()) {
            currentClassTypes.put(variable, childClassTypes.get(z));
            z++;
        }
    }

    Map<Integer, Type> superClassesTypes = new HashMap<>();
    if (superclassType instanceof ParameterizedType) {
        int i = 0;
        for (final Type argType : ((ParameterizedType) superclassType).getActualTypeArguments()) {
            if (argType instanceof TypeVariable) {
                superClassesTypes.put(i, currentClassTypes.getOrDefault(argType, argType));
            } else {
                superClassesTypes.put(i, refineType(argType, currentClassTypes));
            }
            i++;
        }
    }

    if (klass != rootClass) {
        final Class<?> superClass = klass.getSuperclass();
        if (superClass == null)
            throw new GenericsException(String.format("Class [%s] not found on class parent hierarchy [%s]", rootClass, targetClass));
        return analyzeParameterizedTypes(superClass, targetClass, rootClass, paramTypeNumber, superClassesTypes);
    }
    return childClassTypes.get(paramTypeNumber);

}

Example usage:用法示例:

private class SomeClass<A, B, C, D, E, F> {}
private class SomeConfusingClass<A> extends SomeClass<List<Void>[], List<? extends A>[], List<? extends A[][][]>[][][], List<? extends String[]>[], Map<List<? extends A[]>, A[][]>[], A> {}
private class TestClass extends SomeConfusingClass<Void> {}

public static void main(String[] args) throws Exception {
    System.out.println(GenericsUtils.getParameterizedType(TestClass.class, SomeClass.class, 0));
    System.out.println(GenericsUtils.getParameterizedType(TestClass.class, SomeClass.class, 1));
    System.out.println(GenericsUtils.getParameterizedType(TestClass.class, SomeClass.class, 2));
    System.out.println(GenericsUtils.getParameterizedType(TestClass.class, SomeClass.class, 3));
    System.out.println(GenericsUtils.getParameterizedType(TestClass.class, SomeClass.class, 4));
    System.out.println(GenericsUtils.getParameterizedType(TestClass.class, SomeClass.class, 5));
}

And results:结果:

java.util.List<java.lang.Void>[]
java.util.List<? extends java.lang.Void>[]
java.util.List<? extends java.lang.Void[][][]>[][][]
java.util.List<? extends java.lang.String[]>[]
java.util.Map<java.util.List<? extends java.lang.Void[]>, java.lang.Void[][]>[]
class java.lang.Void

Whole code with tests can be found here: https://gist.github.com/GotoFinal/33b9e282f270dbfe61907aa830c27587 or here: https://github.com/GotoFinal/generics-utils/tree/edge-cases-1可以在此处找到带有测试的完整代码: https : //gist.github.com/GotoFinal/33b9e282f270dbfe61907aa830c27587或此处: https : //github.com/GotoFinal/generics-utils/tree/edge-cases-1

Based on OP original answer code, but with most of edge cases covered.基于 OP 原始答案代码,但涵盖了大多数边缘情况。

You can't get generic parameter without inheritence because of generics in java has type erasure : https://docs.oracle.com/javase/tutorial/java/generics/erasure.html 您不能在没有继承的情况下获取通用参数,因为Java中的通用类型具有擦除类型: https : //docs.oracle.com/javase/tutorial/java/generics/erasure.html

You need to do approach which you've been shared in your question. 您需要采取在问题中已经分享的方法。 Using inheritence and ParameterizedType : 使用继承和ParameterizedType:

(Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];

The answer is: Java does not support reified generics, see this feature request from 2004 with lots of duplicates.答案是:Java 不支持具体泛型,请参阅2004 年的此功能请求,其中有很多重复项。 See also:也可以看看:

So unless you want to switch to Kotlin, there is simply nothing you can do because generic type information in Java is available to the compiler only, not during runtime (reified generics).因此,除非您想切换到 Kotlin,否则您无能为力,因为 Java 中的泛型类型信息仅对编译器可用,而不是在运行时(具体化泛型)期间可用。

I am sorry if you do not like the answer, but still it is correct as of Java 13 in early 2020.如果您不喜欢这个答案,我很抱歉,但从 2020 年初的 Java 13 开始,它仍然是正确的。

I think a working solution would be to extend the one proposed by @naikus.我认为一个可行的解决方案是扩展@naikus 提出的解决方案。 It only needs to go up in the hierarchy on the constructor.它只需要在构造函数的层次结构中向上。

import java.lang.reflect.ParameterizedType;

public abstract class AbstractRequestHandler<I,O> {

    protected I input;
    protected O output;

    protected Class<I> inputClass;
    protected Class<O> outputClass;

    protected AbstractRequestHandler() {
        Class<?> clazz = getClass();
        while (!clazz.getSuperclass().equals(AbstractRequestHandler.class)) {
            clazz = clazz.getSuperclass();
        }
        ParameterizedType genericSuperclass = (ParameterizedType) clazz.getGenericSuperclass();
        this.inputClass = (Class<I>) genericSuperclass.getActualTypeArguments()[0];
        this.outputClass = (Class<O>) genericSuperclass.getActualTypeArguments()[1];
    }
}

I've worked to an utility library that offers a method that generally solve the question analyzing recursively all parent classes hierarchy to get a specific generic type.我曾研究过一个实用程序库,该提供了一种方法,该方法通常可以解决递归分析所有父类层次结构以获得特定泛型类型的问题。

It is available on my GitHub project: https://github.com/gregorycallea/generics-utils它可以在我的 GitHub 项目上找到: https : //github.com/gregorycallea/generics-utils

UPDATE : Thanks to @GoToFinal user that with his great effort improved the project covering also several differents complex generics case ( such as GenericArrayType , ParameterizedType and WildcardType ).更新:感谢@GoToFinal用户通过他的巨大努力改进了项目,还涵盖了几种不同的复杂泛型案例(例如GenericArrayTypeParameterizedTypeWildcardType )。 For all details about these improvements see his answer on this question.有关这些改进的所有详细信息,请参阅他对这个问题的回答。


This is the summarized scenario the method works on:这是该方法适用的总结场景:

  1. Suppose you have a parameterized root class with an undefined number of generics defined.假设您有一个参数化的类,定义了未定义数量的泛型。

Example: Let's consider as root class the following " Base " class that defines 3 generics:示例:让我们将以下定义 3 个泛型的“ Base ”类视为根类:

 private class Base<I, E, F> { I var1; E var2; F var3; }

NOTE: To each generic is assigned an index starting from 0. So index mapping for this class is:注意:为每个泛型分配一个从 0 开始的索引。因此该类的索引映射是:

 I = 0 E = 1 F = 2
  1. Suppose this root class have a complex and multi-leveled hierarchy of child classes.假设这个根类有一个复杂的层次的子类层次结构。

Example:例子:

 // Base<I,E,F> // BaseA<G,H> extends Base<H,Boolean,G> // BaseB<T> extends BaseA<T,String> // BaseC<H> extends BaseB<H> // BaseD extends BaseC<Integer> // BaseE extends BaseD // BaseF extends BaseE // BaseG extends BaseF // BaseH<H> extends BaseG<H,Double> // BaseI<T> extends BaseF<T> // BaseL<J> extends BaseI<J> // BaseM extends BaseL<Float> // BaseN extends BaseM

NOTE: Notice that walking the child hierarchy new parameterized classes are defined and also some classes are not parameterized at all注意:请注意,在子层次结构中定义了新的参数化类,并且一些类根本没有参数化

  1. Then suppose you want to choose whatever class on root class child hierarchy and then get the exactly type of a specific generic defined on root class starting from this.然后假设您想选择根类子层次结构上的任何类,然后从这里开始获取在类上定义的特定泛型的确切类型。

Example: You want to know the type of E generic ( with index = 1 ) defined on Base class starting from child class BaseN .示例:您想知道从子类BaseN开始的Base类上定义的E泛型(索引 = 1)的类型。

To do this you can simply execute the GenericsUtils.getParameterizedType method as follow:为此,您可以简单地执行GenericsUtils.getParameterizedType方法,如下所示:

Type targetType = GenericsUtils.getParameterizedType(GenericChildClass.class, RootClass.class, genericRootClassIndex);

Example:例子:

 Type EType = GenericsUtils.getParameterizedType(BaseN.class, Base.class, 1);

I evaluated several cases for this example scenario with unit tests.我使用单元测试评估了此示例场景的几个案例。 Take a look at: https://github.com/gregorycallea/generics-utils/blob/master/src/test/java/com/github/gregorycallea/generics/GenericsUtilsTest.java看看: https : //github.com/gregorycallea/generics-utils/blob/master/src/test/java/com/github/gregorycallea/generics/GenericsUtilsTest.java


About the initial scenario exposed on my question instead we can use this method on AbstractRequestHandler constructor as follow:关于在我的问题中公开的初始场景,我们可以在 AbstractRequestHandler 构造函数上使用此方法,如下所示:

public abstract class AbstractRequestHandler<I,O> {
        I input;
        O output;

        public AbstractRequestHandler(String inputJson) throws GenericsException {
            this.input = new Gson().fromJson(inputJson,GenericsUtils.getParameterizedType(getClass(), AbstractRequestHandler.class, 0));
        }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM