Utilizzo di un tipo generico di qualsiasi sottoclasse nidificata all'interno della sua superclasse astratta

2018-12-09 java generics inheritance reflection hierarchy

Supponiamo di avere la seguente classe java astratta:

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

e la seguente gerarchia di classi figlio:

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

Supponiamo che in un determinato punto della superclasse sia necessario utilizzare il tipo generico, ad esempio per deserializzare sul costruttore la richiesta json sull'oggetto richiesta specifico usando la libreria gson come segue:

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

È necessario il tipo di I generico all'interno della variabile "typeOfI"

Esiste una soluzione globale che consente di ottenere il tipo generico specificato da una classe figlio concreta che rispetti i seguenti vincoli?

  1. Il tipo viene ottenuto in fase di esecuzione indipendentemente dalla gerarchia delle classi figlio (che può anche essere più complessa di quella fornita come esempio in questa domanda)
  2. Lo sviluppatore deve solo definire il generico che estende la superclasse senza specificare manualmente il tipo generico da qualche parte su una classe figlio concreta (ad esempio su metodo o costruttore ignorato)

In modo che se si desidera definire un nuovo figlio concreto che assegni un nuovo valore a un generico, è possibile semplicemente scrivere la seguente classe concreta:

public class StudentRequestHandler extends AbstractUniversityRequestHandler<Student>{

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

}

Ho trovato le seguenti soluzioni ma non rispettano entrambi i vincoli della soluzione richiesta.

Soluzione che rompe il vincolo n ° 2

Una soluzione potrebbe essere quella di definire un metodo astratto sulla superclasse come segue

protected abstract Type getRequestType();

e quindi implementarlo su ogni classe figlio concreta che definisce il generico:

public class StudentRequestHandler extends AbstractUniversityRequestHandler<Student>{

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

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

Quindi il metodo getRequestType () può essere utilizzato sul costruttore nella superclasse di destinazione:

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

Ma anche se funziona indipendentemente dalla gerarchia delle classi figlio ( rispetto del vincolo n. 1 ) lo sviluppatore dovrebbe implementare manualmente un metodo astratto su ogni classe figlio concreta.

Soluzione che rompe il vincolo n ° 1

Se la gerarchia è semplice avendo solo un figlio diretto che si estende dalla superclasse di destinazione , come ad esempio:

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

una soluzione funzionante è stata proposta da @naikus ( https://stackoverflow.com/users/306602/naikus ) sul seguente thread stackoverflow: Usi un tipo generico di una sottoclasse all'interno della sua superclasse astratta?

Tuttavia, ciò non funziona se la classe concreta non è un figlio diretto della superclasse che definisce i generici (come quello proposto come esempio in questa domanda).

Answers

La risposta è: Java non supporta generici reificati, vedere questa richiesta di funzionalità del 2004 con molti duplicati. Guarda anche:

Quindi, a meno che tu non voglia passare a Kotlin, semplicemente non puoi fare nulla perché le informazioni di tipo generico in Java sono disponibili solo per il compilatore, non durante il runtime (generici reificati).

Mi dispiace se non ti piace la risposta, ma è ancora corretta a partire da Java 13 all'inizio del 2020.

Penso che una soluzione funzionante sarebbe quella di estendere quella proposta da @naikus. Deve solo salire nella gerarchia sul costruttore.

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

Modifica: dopo aver letto la tua risposta e testato molti altri possibili casi, ho deciso di modificare il tuo codice e riscriverlo per supportare tutti gli altri possibili casi limite per includere il monitoraggio dei generici nidificati in profondità all'interno di altri tipi generici.

Purtroppo per supportare tutti i casi abbiamo bisogno di molto più codice di quello che ci hai fornito, i generici sono molto complicati, come considerare una classe come questa:

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

Per iniziare a farlo, è necessario disporre della propria implementazione di tipi generici java per poter successivamente costruire tipi come List<String>[] quanto non è possibile creare dinamicamente tale tipo con API java non elaborate.
Questo è un modo piuttosto popolare di gestire generici in librerie del genere, puoi vedere cose simili nella libreria di jackson e molte altre.
Quindi abbiamo bisogno dell'implementazione di GenericArrayType , ParameterizedType e 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);
    }
} 

Puoi praticamente copiarli da JDK e fare solo un po 'di pulizia.

La prossima utility di cui abbiamo bisogno è una funzione da validare alla fine se abbiamo fatto tutto bene, come se non volessimo restituire Map<List<? extends X>[]> dove X è ancora risolto 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;
}

La semplice funzione ricorsiva lo farà per noi. Controlliamo solo per ogni possibile tipo generico e controlliamo se anche ogni membro è definito, e se non troveremo alcuni TypeVariable nascosti, stiamo bene.
La funzione principale può rimanere la stessa del tuo codice, alla fine modificheremo solo un controllo per utilizzare la nostra nuova funzione:

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

Ora consente di lavorare sul nostro principale

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

funzione, l'accattonaggio rimane lo stesso, raccogliamo tutti i TypeVariable in una semplice mappa, mantenendo le informazioni già raccolte dal ciclo precedente nella classe precedente.

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

Quindi il nostro ciclo raccoglie e perfeziona i nostri argomenti di tipo:

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

Ci sono 2 percorsi per ogni argomento di tipo, se è TypeVariable continuiamo a seguirlo, e se è qualcos'altro cerchiamo di "perfezionarlo" da qualsiasi possibile riferimento a TypeVariable . Questo è il processo più complicato di questo codice, ed è per questo che abbiamo bisogno di tutte queste classi sopra.
Partiamo da questo semplice metodo di invio ricorsivo che gestisce tutti i tipi possibili:

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

E piccolo metodo di utilità per eseguirlo su array di tipi:

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

Ogni tipo va alla propria funzione, o se il suo TypeVariable prendiamo solo uno risolto dalla mappa. Si noti che questo può restituire null e non l'ho gestito qui. Questo potrebbe essere migliorato in seguito. Per le classi non abbiamo bisogno di fare nulla, quindi possiamo semplicemente restituire la classe stessa.

Per GenericArrayType dobbiamo prima scoprire quante dimensioni potrebbe avere tale array (questo potrebbe essere gestito anche dalla ricorsione nel nostro metodo di affinamento, ma secondo me è un po 'più difficile eseguire il debug secondo me):

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

Quindi vogliamo estrarre quel tipo di componente nidificato di array, quindi per List<A>[][][] vogliamo solo List<A> :

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

E quindi dobbiamo perfezionare questo tipo, quindi il nostro List<A> passerà ad esempio a List<String> :

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

E ricostruisci la nostra struttura generica usando il tipo raffinato, quindi la nostra List<String> creata tornerà alla 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");
    }
}

E l'intera funzione è simile a questa:

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

Per ParameterizedType molto più semplice, perfezioniamo semplicemente gli argomenti di tipo e creiamo una nuova istanza di ParameterizedType con questi argomenti perfezionati:

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

Lo stesso per 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);
}

E questo ci lascia con l'intera funzione di analisi simile a questa:

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

}

Esempio di utilizzo:

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

E risultati:

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

L'intero codice con i test può essere trovato qui: https://gist.github.com/GotoFinal/33b9e282f270dbfe61907aa830c27587 o qui: https://github.com/GotoFinal/generics-utils/tree/edge-cases-1

Basato sul codice di risposta originale OP, ma con la maggior parte dei casi limite coperti.

Ho lavorato a una libreria di utilità che offre un metodo che generalmente risolve la domanda analizzando ricorsivamente la gerarchia di tutte le classi parent per ottenere un tipo generico specifico.

È disponibile sul mio progetto GitHub: https://github.com/gregorycallea/generics-utils

AGGIORNAMENTO : Grazie all'utente @GoToFinal che con il suo grande sforzo ha migliorato il progetto coprendo anche diverse differenze complesse caso generics (come GenericArrayType , ParameterizedType e WildcardType ). Per tutti i dettagli su questi miglioramenti vedere la sua risposta a questa domanda.


Questo è lo scenario riassunto su cui funziona il metodo:

  1. Supponiamo di avere una classe radice con parametri con un numero indefinito di generici definiti.

Esempio: consideriamo come classe radice la seguente classe " Base " che definisce 3 generici:

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

NOTA: a ciascun generico è assegnato un indice a partire da 0. Quindi la mappatura dell'indice per questa classe è:

I = 0
E = 1
F = 2
  1. Supponiamo che questa classe radice abbia una gerarchia complessa e multilivello di classi figlio.

Esempio:

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

NOTA: si noti che camminando nella gerarchia figlio sono definite nuove classi con parametri e anche alcune classi non sono affatto parametrizzate

  1. Supponiamo quindi di voler scegliere qualsiasi classe nella gerarchia figlio della classe radice e quindi ottenere esattamente il tipo di un generico specifico definito sulla classe radice a partire da questo.

Esempio: Si desidera conoscere il tipo di E generico (con indice = 1) definito sulla classe Base a partire dalla classe figlio BaseN .

Per fare ciò puoi semplicemente eseguire il metodo GenericsUtils.getParameterizedType come segue:

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

Esempio:

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

Ho valutato diversi casi per questo scenario di esempio con unit test. Dare un'occhiata a: https://github.com/gregorycallea/generics-utils/blob/master/src/test/java/com/github/gregorycallea/generics/GenericsUtilsTest.java


A proposito dello scenario iniziale esposto alla mia domanda, invece, possiamo usare questo metodo sul costruttore AbstractRequestHandler come segue:

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

Related