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

时间:2018-12-08 19:14:25

标签: java generics reflection

假设您具有以下抽象的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>{...}

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

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?

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

5 个答案:

答案 0 :(得分:2)

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

要想支持所有情况,我们需要的代码比您提供的要多得多,泛型非常棘手,就像考虑这样的类:

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<String>[]之类的类型,因为无法使用原始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<List<? extends X>[]>仍未解决X的{​​{1}}:

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

每个类型参数都有2条路径,如果它的TypeVariable我们一直跟踪它,并且如果有其他任何情况,我们尝试从对 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++; } } 的任何可能引用中“优化”它。这是这段代码最复杂的过程,这就是为什么我们需要上面所有这些类的原因。 我们从处理所有可能类型的简单递归调度方法开始:

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; } 我们只是从映射中获取已解析的类型。请注意,这可以返回null,在这里我没有处理。以后可以改进。对于类,我们无需执行任何操作,因此我们只需返回类本身即可。

对于TypeVariable,我们需要首先找出该数组可能有多少维(也可以通过我们的精简方法中的递归来处理,但是在我看来,这很难调试):

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<A>[][][]

List<A>

然后我们需要改进此类型,因此我们的private static Type getArrayNestedComponentType(GenericArrayType genericArrayType) { GenericArrayType currentArrayLevel = genericArrayType; while (currentArrayLevel.getGenericComponentType() instanceof GenericArrayType) { currentArrayLevel = (GenericArrayType) currentArrayLevel.getGenericComponentType(); } return currentArrayLevel.getGenericComponentType(); } 将更改为List<A>

List<String>

并使用精炼类型重建通用结构,因此我们创建的 Type arrayComponentType = refineType(getArrayNestedComponentType(genericArrayType), typeVariablesMap); 将更改回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");
    }
}

为了简化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));
}

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

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

答案 1 :(得分:0)

由于Java中的泛型具有类型擦除:https://docs.oracle.com/javase/tutorial/java/generics/erasure.html

,因此无法获得没有继承的泛型参数

您需要采取在问题中已经分享的方法。使用继承和ParameterizedType:

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

答案 2 :(得分:0)

答案是:Java不支持统一的泛型,请参阅this feature request from 2004,其中包含大量重复项。另请参阅:

因此,除非您想切换到Kotlin,否则您将无能为力,因为Java中的泛型类型信息仅对编译器可用,而不是在运行时(经过修改的泛型)可用。

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

答案 3 :(得分:0)

我认为一个可行的解决方案是扩展@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 :(得分:0)

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

它在我的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. 假设此根类具有子类的 complex 多级层次结构。

示例:

// 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 类上定义的特定泛型的确切类型。

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

为此,您只需执行 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));
        }
}