【问题标题】:Using a generic type of any nested subclass within its abstract superclass在其抽象超类中使用任何嵌套子类的泛型类型
【发布时间】:2019-05-10 04:38:15
【问题描述】:

假设你有以下抽象java类:

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

以及以下子类层次结构:

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>{...}

假设您需要在超类的给定点使用泛型类型,例如,为了在构造函数上将请求 json 反序列化为使用 gson 库的特定请求对象,如下所示:

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

您需要变量“typeOfI”中的泛型 I 类型

是否有一个全局解决方案允许获取由遵守以下约束的具体子类指定的泛型类型?

  1. 类型是在运行时获得的不管子类层次结构(这也可能更复杂,作为这个问题的例子给出的那个)
  2. 开发人员只需要定义泛型扩展超类无需在具体子类的某处手动指定泛型类型(例如在重写的方法或构造函数上)

因此,如果您想定义一个新的具体子代,为泛型分配一个新值,您可以编写以下具体类,例如:

public class StudentRequestHandler extends AbstractUniversityRequestHandler<Student>{

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

}

我找到了以下解决方案,但它们不尊重所要求的解决方案限制。

打破约束 n°2 的解决方案

一种解决方案是在超类上定义一个抽象方法,如下所示

protected abstract Type getRequestType();

然后在每个定义泛型的具体子类上实现它:

public class StudentRequestHandler extends AbstractUniversityRequestHandler<Student>{

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

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

那么getRequestType()方法可以用在目标超类的构造函数上:

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

但即使不管子类层次结构如何(尊重约束 n°1),开发人员也应该在每个具体的子类上手动实现一个抽象方法。

打破约束 n°1 的解决方案

如果层次结构很简单,只有一个从目标超类扩展的直接子级,例如:

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

@naikus (https://stackoverflow.com/users/306602/naikus) 在以下 stackoverflow 线程上提出了一个可行的解决方案: Using a generic type of a subclass within it's abstract superclass?

但是,如果具体类不是定义泛型的超类的直接子类(作为此问题的示例提出的示例),则此方法不起作用。

【问题讨论】:

  • protected abstract Class&lt;I&gt; getRequestType(); 怎么样,那么仍然有人需要定义方法,但您不再需要担心有人会传递无效类型。否则,只需使用反射从超类中读取类型。
  • @GotoFinal 您报告了一个与我在问题中指出的类似的解决方案,即打破约束 n°2 的解决方案。即将从超类中读取类型,我报告了一个仅对直接孩子有效的解决方案。相反,我们需要一个可以通用地处理复杂层次结构的工具
  • 在这里,我使用有效的扫描方法添加了答案 - 有一些小的未实现的极端情况,但我认为没有人会定义这样的类。

标签: java generics inheritance reflection hierarchy


【解决方案1】:

编辑: 在阅读了您的答案并测试了许多其他可能的情况后,我决定编辑您的代码并重新编写它以支持所有其他可能的边缘情况,包括跟踪深度嵌套的泛型在其他泛型类型中。

遗憾的是,为了支持所有情况,我们需要比您提供的代码多得多的代码,泛型非常棘手,比如考虑这样的类:

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> {}

要开始这样做,我们需要拥有自己的 java 泛型类型实现,以便以后能够构造像 List&lt;String&gt;[] 这样的类型,因为无法使用原始 java API 动态创建此类类型。
这是在这样的库中处理泛型的非常流行的方法,您可以在 jackson 库中看到类似的东西等等。
所以我们需要实现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);
    }
} 

您基本上可以从 JDK 复制它们并进行一些清理。

我们需要的下一个实用程序是一个函数,用于在最后验证我们是否做对了所有事情,例如我们不想返回 Map&lt;List&lt;? extends X&gt;[]&gt;,其中 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;
}

简单的递归函数将为我们做到这一点。我们只检查每个可能的泛型类型,并检查它的每个成员是否也被定义,除非我们能找到一些隐藏的TypeVariable,否则我们很好。
主要功能可以与您的代码中的相同,我们只会在最后编辑那一项检查以使用我们的新功能:

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;
}

现在让我们开始我们的主要工作

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

函数,乞求保持不变,我们将所有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++;
        }
    }

然后我们的循环收集和改进我们的类型参数:

    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++;
        }
    }

每个类型参数有 2 条路径,如果它的 TypeVariable 我们只是继续跟踪它,如果它有其他任何东西,我们会尝试从任何可能对 TypeVariable 的引用中“优化”它。这是这段代码最复杂的过程,这也是我们需要上面所有这些类的原因。
我们从这个处理所有可能类型的简单递归调度方法开始:

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);
}

以及在类型数组上运行它的小实用方法:

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;
}

每种类型都有自己的函数,或者如果它的TypeVariable 我们只是从映射中获取已解析的一个。注意这个可以返回null,这里我没有处理。这可以在以后改进。对于类,我们不需要做任何事情,所以我们可以返回类本身。

对于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;
}

然后我们要提取数组的嵌套组件类型,所以对于List&lt;A&gt;[][][],我们只需要List&lt;A&gt;

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

然后我们需要细化这个类型,所以我们的List&lt;A&gt;会变成例如List&lt;String&gt;

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

并使用细化类型重建我们的通用结构,因此我们创建的List&lt;String&gt; 将变回List&lt;String&gt;[][][]

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");
    }
}

整个函数看起来像这样:

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);
}

对于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());
}

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);
}

这让我们的整个分析函数看起来像这样:

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);

}

示例用法:

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));
}

结果:

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

可以在此处找到带有测试的完整代码:https://gist.github.com/GotoFinal/33b9e282f270dbfe61907aa830c27587 或此处:https://github.com/GotoFinal/generics-utils/tree/edge-cases-1

基于 OP 原始答案代码,但涵盖了大多数边缘情况。

【讨论】:

  • 好的,做了一些小修改,从主代码中提取了至少 2 个方法,使其更具可读性,我认为这样的例子就足够了。
  • @GotoFinal 感谢您的分析。我知道你的代码有点乱(正如你所说),它可能会被修改为 usable 但我认为无论如何它在这里可能是有效的,因为我们只是想证明一个解决方案是否可行可用于我的问题。看看我的答案,我为此使用了实用方法。将您的代码与我的代码进行比较,方法非常相似。我还没有机会用我的答案示例场景测试您的代码。我会尽快尝试,如果它也有效(因为我无法将赏金分配给我自己的答案)我会将赏金分配给您的答案
  • 此时完全重写了我的答案,因为我也几乎完全重写了您的解决方案来处理所有这些情况。我也解释得更好。玩这样的泛型很有趣。现在它应该可以处理几乎所有可以处理的事情了。
  • 对于怀疑您投了反对票,我深表歉意。 downvote 和你的 cmets 大约在同一时间发生。不过,我的回答不是无效。我说 Java 不具有具体化的泛型,我并没有说什么都可以做以以某种方式实现所要求的。我只是相信像你的解决方案这样的东西不应该在工作中使用,就像你确认你也不会这样做一样。无论如何,我对您在解决方案中付出的努力以使其具有通用性印象深刻,这就是我赞成它的原因。在 OP 为他的问题设定的范围内做得很好!
  • 干得好@GotoFinal!我已将您的拉取请求合并到我的 Github 项目中。如果可能,请在作者部分的根 pom.xml 上提交另一个自我介绍。令人惊讶的是,如何从我的基本想法开始,然后从我解决通用通用案例的项目(在我的问题上报告)你改进了很多,以涵盖如此不同的复杂通用案例。现在社区有一个很好的库,可以解决我的问题以及更复杂的通用案例!
【解决方案2】:

答案是:Java 不支持具体化的泛型,请参阅this feature request from 2004,其中有很多重复项。另见:

因此,除非您想切换到 Kotlin,否则您根本无能为力,因为 Java 中的泛型类型信息仅对编译器可用,而不是在运行时(具体化泛型)期间可用。

如果您不喜欢这个答案,我很抱歉,但从 2020 年初的 Java 13 开始,它仍然是正确的。

【讨论】:

  • 您仍然可以读取泛型类型的超类、字段和方法,尽管它们可以在编译时被剥离,但默认情况下它们会保留。您只能获得像T.class 这样的泛型类型的运行时值。但是在这个问题中它是不需要的。
  • @kriegaex 如果我有这样的真实用例,我会接受你的回答并感谢你的参考!这个我就不多说了:D
  • 答案是不正确的,因为它假设运行时没有通用信息。我的回答有点老套,但它仍然在类似 jackson 的库中以类似的形式使用
  • @kriegaex 感谢您的有效分析,当然 Java 不支持具体化的泛型。我不知道为什么有人会否决您报告正确信息的答案!无论如何,正如您向 Bibek 指出的那样,我应该选择一个可以实际解决我的问题目标的答案,并且(正如 GotoFinal 所报告的那样)我们仍然可以阅读超类的泛型类型,这是解决问题的起点。我无法为赏金选择自己的答案,即使它通常可以解决,所以我应该从其他实际上有有效结果的人中选择最好的答案
【解决方案3】:

我认为一个可行的解决方案是扩展@naikus 提出的解决方案。它只需要在构造函数的层次结构中向上。

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];
    }
}

【讨论】:

  • 是的,这基本上是解决方案可以采用的方法。几天前我开始工作,从类似的方法开始,到一个实用程序库。如果它能够普遍解决我的问题,我会分享它。
【解决方案4】:

我研究了一个实用程序库,它提供了一种一般地解决问题递归地分析所有父类层次结构以获得特定泛型类型的方法。

在我的 GitHub 项目上可用:https://github.com/gregorycallea/generics-utils

更新:感谢 @GoToFinal 用户通过他的巨大努力改进了项目,涵盖了几个不同的复杂 泛型案例(例如GenericArrayTypeParameterizedTypeWildcardType)。 有关这些改进的所有详细信息,请参阅他对这个问题的回答。


这是该方法适用的总结场景:

  1. 假设您有一个参数化的 root 类,其中定义了未定义数量的泛型。

示例:让我们将以下定义 3 个泛型的“Base”类视为根类:

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

注意:为每个泛型分配一个从 0 开始的索引。因此该类的索引映射为:

I = 0
E = 1
F = 2
  1. 假设这个根类有一个复杂多层次的子类层次结构。

示例:

// 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

注意:请注意,遍历子层次结构新的参数化类已定义,并且某些类根本没有参数化

  1. 然后假设您想选择根类子层次结构中的任何类,然后从此开始获取在 root 类上定义的特定泛型的确切类型。

示例: 您想知道从子类 BaseN 开始在 Base 类上定义的 E 泛型(索引 = 1)的类型。

为此,您只需执行 GenericsUtils.getParameterizedType 方法,如下所示:

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

示例:

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

我用单元测试评估了这个示例场景的几个案例。 看一眼: https://github.com/gregorycallea/generics-utils/blob/master/src/test/java/com/github/gregorycallea/generics/GenericsUtilsTest.java


关于我的问题中暴露的初始场景,我们可以在 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));
        }
}

【讨论】:

  • 如果类型不是简单类型,你的代码将会失败,比如List&lt;String&gt;,你也不应该像这样从类型中获取一个类,总是有方法可以做到这一点,你应该使用instanceof检查检查它是否是一个类、TypeVariable、ParametrizedType、GenericArrayType 等,并使用适当的方法来提取类。但最好只对类型本身进行操作。在与我类似的情况下,它也会失败。
  • 但是map的用法看起来好多了,没想到这里居然可以用
  • 如果您不介意我更改了您的代码以添加缺失的案例并添加到我的答案中,因为答案指向某个存储库也不是一个好主意,因为它可能有一天会消失。跨度>
  • 我将很快更新我的答案,增加一个通用数组的复杂案例
  • 完成了,但还有一个没有正确处理的案例。
猜你喜欢
  • 1970-01-01
  • 2023-03-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-22
  • 1970-01-01
  • 1970-01-01
  • 2013-09-13
相关资源
最近更新 更多