在其抽象超类中使用任何嵌套子类的泛型类型 [英] Using a generic type of any nested subclass within its abstract superclass

查看:79
本文介绍了在其抽象超类中使用任何嵌套子类的泛型类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设您具有以下抽象的Java类:

Suppose you have the following abstract java class:

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,如下所示:

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:

public AbstractRequestHandler(final String inputJson) {
        input = new Gson().fromJson(inputJson,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. 该类型是在运行时获取的,无论子类的层次结构如何(对于该问题的示例,该类型也可能会更复杂)
  2. 开发人员只需定义扩展超类的泛型,而无需在具体的子类上的某个地方手动指定泛型类型(例如,在重写的方法或构造函数上)
  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.

一种解决方案可能是在父类上定义如下的抽象方法

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

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

Then the getRequestType() method can be used on constructor on the target superclass:

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

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

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.

如果层次结构简单,仅具有从目标超类扩展的直接子对象,例如:

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

@naikus( https://stackoverflow.com/users/306602/naikus 提出了一种可行的解决方案)在以下stackoverflow线程上: 在其抽象中使用子类的泛型超类?

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?

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

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

推荐答案

在阅读了您的答案并测试了许多其他可能的情况后,我决定编辑您的代码并重新编写支持所有其他可能的极端情况,包括跟踪嵌套在其他泛型类型内部的泛型.

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

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

要开始执行此操作,我们还需要拥有自己的Java泛型类型的实现,以便以后能够构造类似List<String>[]的类型,因为无法使用原始Java API动态创建此类.
这种在类库中处理泛型的方法非常流行,您可以在jackson库等中看到类似的内容.
因此,我们需要实现GenericArrayTypeParameterizedTypeWildcardType:

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.
This is pretty popular way of handling generic in libraries like that, you can see similar thing in jackson library and many more.
So we need implementation of GenericArrayType, ParameterizedType and WildcardType:

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复制它们,然后进行一些清理.

You can basically copy them from JDK and just do some cleanup.

如果我们做对了所有事情,那么我们需要的下一个实用工具是最后验证的函数,例如我们不想返回Map<List<? extends X>[]>,其中X仍未解决TypeVariable:

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>[]> where X is still not resolved 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,否则就很好了.
主要功能可以与您的代码保持不变,我们只需要在最后编辑一张支票即可使用我们的新功能:

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.
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:

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 { 

功能,乞求保持不变,我们将所有TypeVariable收集到简单映射中,并保留来自上一类的上一循环中已收集的信息.

function, the begging stays the same, we collect all TypeVariable to simple map, keeping already collected information from previous loop on previous class.

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

每个类型参数有2条路径,如果它的TypeVariable我们一直跟踪它,并且如果有其他问题,我们尝试从对TypeVariable的任何可能引用中优化"它.这是这段代码最复杂的过程,这就是为什么我们需要上面所有这些类的原因. 我们从处理所有可能类型的简单递归调度方法开始:

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

每种类型都属于自己的函数,或者如果它的TypeVariable我们只是从map中获取已解析的一种.请注意,这可以返回null,而我在这里未进行处理.稍后可以对此进行改进.对于类,我们无需执行任何操作,因此我们只需要返回类本身即可.

Each type goes to own function, or if its TypeVariable we just fetch resolved one from map. Note that this can return null, and I did not handle it here. This could be improved later. For classes we don't need to do anything so we can just return class itself.

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

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

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

Then we want to extract that nested component type of array, so for List<A>[][][] we want just 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>:

And then we need to refine this type, so our List<A> will change to eg List<String>:

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

并使用精炼类型重建通用结构,因此我们创建的List<String>将更改回List<String>[][][]:

And rebuild our generic structure using refined type, so our created List<String> will change back to 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实例:

For ParameterizedType its much simpler, we just refine type arguments, and create new ParameterizedType instance with these refined arguments:

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

这使我们拥有完整的分析功能,如下所示:

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

}

用法示例:

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

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

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

Based on OP original answer code, but with most of edge cases covered.

这篇关于在其抽象超类中使用任何嵌套子类的泛型类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆