Ottieni un tipo generico di classe in fase di esecuzione


508

Come posso raggiungere questo obiettivo?

public class GenericClass<T>
{
    public Type getMyType()
    {
        //How do I return the type of T?
    }
}

Tutto ciò che ho provato finora restituisce sempre il tipo Objectanziché il tipo specifico utilizzato.


3
@SamuelVidal Questo non è .NET, questo è Java.
Frontear,

Risposte:


317

Come altri hanno già detto, è possibile solo attraverso la riflessione in determinate circostanze.

Se hai davvero bisogno del tipo, questo è il solito schema (sicuro per il tipo):

public class GenericClass<T> {

     private final Class<T> type;

     public GenericClass(Class<T> type) {
          this.type = type;
     }

     public Class<T> getMyType() {
         return this.type;
     }
}

58
Mi piace questa risposta, ma è un po 'complicato da istanziare: GenericClass <AnotherClass> g = new GenericClass <AnotherClass> (AnotherClass.class);
Eliseo Ocampos,

1
È ancora più dettagliato se usi un approccio dao / factory / manager. Foo foo1 = GetDao<Foo>(Foo.class).get(Foo.class, 1)
djmj,

2
È vero, ma non funziona in tutti i casi come i bean remoti senza stato che sono istanziati dal contenitore / riflessione.
djmj,

6
Proprio come seguito al mio commento precedente - dopo tanto dolore giocando con la riflessione, ho finito per usare questa risposta.
Tomáš Zato - Ripristina Monica

18
È possibile aggirare il riferimento superfluo fornendo un metodo di fabbrica statico generico. Qualcosa di simile public static <T> GenericClass<T> of(Class<T> type) {...}e quindi chiamare come tale: GenericClass<String> var = GenericClass.of(String.class). Un po 'più carino.
Joeri Hendrickx,

263

Ho visto qualcosa del genere

private Class<T> persistentClass;

public Constructor() {
    this.persistentClass = (Class<T>) ((ParameterizedType) getClass()
                            .getGenericSuperclass()).getActualTypeArguments()[0];
 }

nella sospensione GenericDataAccessObjects Esempio


84
Questa tecnica funziona laddove il parametro type è definito nella superclasse immediata, ma non riesce se il parametro type è definito altrove nella gerarchia dei tipi. Per gestire casi più complessi si può usare qualcosa come TypeTools . I documenti includono un esempio di DAO generico più sofisticato.
Jonathan,

25
Questo restituisce solo i parametri di tipo effettivi utilizzati quando un CLASS attrezzi / estende qualcosa che ha le dichiarazioni generiche, non restituisce i parametri di tipo effettivi utilizzati quando un GRADO viene creata un'istanza. In altre parole, PUO ' dire che in class A implements Comparable<String>, il parametro del tipo effettivo è String, ma NON PUO' dirlo in Set<String> a = new TreeSet<String>(), il parametro del tipo effettivo è String. In effetti, le informazioni sui parametri del tipo vengono "cancellate" dopo la compilazione, come spiegato in altre risposte.
Siu Ching Pong -Asuka Kenji-

32
Ricevo java.lang.Class cannot be cast to java.lang.reflect.ParameterizedTypequesta risposta.
Tomáš Zato - Ripristina Monica

4
Questo approccio può essere raggiunto anche usando Class-Matela gente di Jackson. Ho scritto un articolo qui gist.github.com/yunspace/930d4d40a787a1f6a7d1
yunspace

5
@ TomášZato Chiamare semplicemente il codice sopra ha restituito la stessa eccezione per me. So che è un po 'tardi, ma comunque, nel mio caso, ho dovuto chiamare (Class<T>) ((ParameterizedType)getClass().getSuperclass().getGenericSuperclass()).getActualTypeArguments()per arrivare a argomenti di tipo reale.
AndrewMcCoist,

99

I generici non sono reificati in fase di esecuzione. Ciò significa che le informazioni non sono presenti in fase di esecuzione.

L'aggiunta di generici a Java mantenendo allo stesso tempo la compatibilità con le versioni precedenti era un tour-de-force (puoi vedere il documento fondamentale a riguardo: Rendere il futuro sicuro per il passato: aggiungere genericità al linguaggio di programmazione Java ).

C'è una ricca letteratura sull'argomento, e alcune persone non sono soddisfatte dello stato attuale, altre dicono che in realtà è un'esca e non ce n'è davvero bisogno. Puoi leggere entrambi i link, li ho trovati piuttosto interessanti.


179
Naturalmente non siamo soddisfatti, .NET ha un meccanismo di gestione generico molto migliore
Pacerier

6
@Pacerier: ma i generici reificati da soli non porterebbero Java al livello di .NET. Il tipo di valore di un codice specializzato per questi è almeno altrettanto importante per il motivo per cui .NET è migliore nell'area generici.
Joachim Sauer,

@JoachimSauer, sì tipi di valore. Ho sempre desiderato quelli a Java. Tra cosa intendi per codice specializzato?
Pacerier,

3
@ spaaarky21 No, durante la compilazione vengono rimossi parametri di tipo generico (cosiddetto "cancellazione", puoi cercarlo su Google). Il trucco nella risposta di FrVaBe funziona solo se i parametri di tipo della superclasse sono conosciuti staticamente (vedi il primo commento di Johnathn)
ewernli,

1
La cancellazione del tipo Java è un difetto di progettazione storica; più codice è stato scritto per aggirarlo che non è stato scritto per implementarlo.
Abhijit Sarkar,

68

Usa la guava.

import com.google.common.reflect.TypeToken;
import java.lang.reflect.Type;

public abstract class GenericClass<T> {
  private final TypeToken<T> typeToken = new TypeToken<T>(getClass()) { };
  private final Type type = typeToken.getType(); // or getRawType() to return Class<? super T>

  public Type getType() {
    return type;
  }

  public static void main(String[] args) {
    GenericClass<String> example = new GenericClass<String>() { };
    System.out.println(example.getType()); // => class java.lang.String
  }
}

Qualche tempo fa, ho pubblicato alcuni esempi completi tra cui classi astratte e sottoclassi qui .

Nota: ciò richiede di creare un'istanza di una sottoclasse inGenericClass modo che possa associare correttamente il parametro type. Altrimenti restituirà il tipo come T.


3
Si noti che creo una sottoclasse anonima vuota (vedere le due parentesi graffe alla fine). Questo utilizza la riflessione per combattere la cancellazione del tipo di runtime di Java. Puoi saperne di più qui: code.google.com/p/guava-libraries/wiki/ReflectionExplained
Cody A. Ray

3
@ CodyA.Ray Il tuo codice genera a java.lang.IllegalArgumentException: class com.google.common.reflect.TypeToken isn't parameterized. Quindi ho cambiato la linea new TypeToken(getClass()) { }in new TypeToken<T>(getClass()) { }. Ora, il codice funziona bene, ma Type è ancora 'T'. Vedi questo: gist.github.com/m-manu/9cda9d8f9d53bead2035
Manu Manjunath

3
@Dominik Vedi l'esempio aggiornato che puoi copiare e incollare per testarti. Ho anche aggiunto una nota che chiarisce che è necessario creare un'istanza di una sottoclasse (come mostrato). Come consiglio generale di etichetta, ti preghiamo di leggere tutti gli articoli collegati e i relativi javadocs prima di accusare un poster di "pio desiderio". Ho usato più volte un codice di produzione simile. Gli aiutanti di Guava che sto dimostrando sono destinati a questo esatto caso d'uso e i loro javadocs mostrano quasi una risposta esatta a questa domanda. docs.guava-libraries.googlecode.com/git/javadoc/com/google/…
Cody A. Ray

1
Questa risposta funziona perfettamente con Java8: ho un'interfaccia e una classe di fabbrica per emettere vari tipi e volevo che gli utenti fossero in grado di determinare facilmente il tipo. Nella mia interfaccia ho aggiunto il metodo predefinito: default Type getParameterType() { final TypeToken<T> typeToken = new TypeToken<T>(getClass()) {}; final Type type = typeToken.getType(); return type; }
Richard Sand,

2
@ CodyA.Ray Dato che questo funziona solo con le sottoclassi di GenericClass, dovresti creare quella classe in abstractmodo da non compilare un uso sbagliato.
Martin,

37

Certo che puoi.

Java non utilizza le informazioni in fase di esecuzione, per motivi di compatibilità con le versioni precedenti. Ma l'informazione è effettivamente presente come metadati e sono accessibili tramite reflection (ma non sono ancora utilizzate per il controllo del tipo).

Dall'API ufficiale:

http://download.oracle.com/javase/6/docs/api/java/lang/reflect/ParameterizedType.html#getActualTypeArguments%28%29

Tuttavia , per il tuo scenario non vorrei usare la riflessione. Personalmente sono più propenso a usarlo per il codice del framework. Nel tuo caso aggiungerei semplicemente il tipo come parametro del costruttore.


10
getActualTypeArguments restituisce solo gli argomenti di tipo per la classe immediata. Se hai una gerarchia di tipi complessi in cui T potrebbe essere parametrizzato in qualsiasi punto della gerarchia, dovrai fare un po 'di lavoro per capire di cosa si tratta. Questo è più o meno ciò che fa TypeTools .
Jonathan,

36

I generici Java sono per lo più tempo di compilazione, ciò significa che le informazioni sul tipo vengono perse in fase di esecuzione.

class GenericCls<T>
{
    T t;
}

sarà compilato in qualcosa del genere

class GenericCls
{
   Object o;
}

Per ottenere le informazioni sul tipo in fase di esecuzione è necessario aggiungerle come argomento del ctor.

class GenericCls<T>
{
     private Class<T> type;
     public GenericCls(Class<T> cls)
     {
        type= cls;
     }
     Class<T> getType(){return type;}
}

Esempio:

GenericCls<?> instance = new GenericCls<String>(String.class);
assert instance.getType() == String.class;

9
private final Class<T> type;
naXa,

Come posso creare un tipo di array da esso:Type t = //String[]
Pawel Cioch,

@PawelCioch java.lang.reflect.Array.newInstance (elementtype, length); spero che questo aiuti (javadoc può essere trovato qui docs.oracle.com/javase/8/docs/api/java/lang/reflect/… )
josefx

@PawelCioch ha perso un .getClass () per ottenere il tipo dall'array creato. Non sembra esserci un modo diretto per ottenere una classe di array. La maggior parte delle raccolte Java usa invece Object [] invece.
josefx,

23
public abstract class AbstractDao<T>
{
    private final Class<T> persistentClass;

    public AbstractDao()
    {
        this.persistentClass = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass())
                .getActualTypeArguments()[0];
    }
}

3
Sto votando questa risposta perché è una soluzione che funziona per la domanda che viene posta. Tuttavia, per coloro che vogliono navigare verso l'alto nella gerarchia di classi come me con più di una classe generica, questo non funzionerà. Perché otterrai java.lang.object invece della classe reale.
KMC

3
Questa soluzione funziona SOLO se la classe che contiene il tipo generico è ABSTRACT
BabaNew

Non ha funzionato con la mia lezione astratta in Java 11
JRA_TLL

@JRA_TLL apparentemente hai fatto qualcosa di sbagliato. L'ho appena usato con Java 12 e funziona come un fascino.
Googie

Se si desidera spostarsi nella gerarchia della vista verso l'alto, è possibile eseguire il cast di GenericSuperclass nella classe <*> e ottenere il genericoSuperclass. Preferibilmente in un ciclo.
fupduck,

21

Ho usato il seguente approccio:

public class A<T> {

    protected Class<T> clazz;

    public A() {
        this.clazz = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    }

    public Class<T> getClazz() {
        return clazz;
    }
}

public class B extends A<C> {
   /* ... */
    public void anything() {
       // here I may use getClazz();
    }
}

9
Ho ottenuto "Eccezione nel thread" principale "java.lang.ClassCastException: java.lang.Class non può essere trasmesso a java.lang.reflect.ParameterizedType" con questo codice di esempio
yuyang

16

Non credo che tu possa, Java usa la cancellazione del tipo durante la compilazione, quindi il tuo codice è compatibile con le applicazioni e le librerie create pre-generiche.

Da Oracle Docs:

Digitare la cancellazione

I generici sono stati introdotti nel linguaggio Java per fornire controlli di tipo più rigorosi in fase di compilazione e supportare la programmazione generica. Per implementare generics, il compilatore Java applica la cancellazione del tipo a:

Sostituisci tutti i parametri di tipo in tipi generici con i loro limiti o Oggetto se i parametri di tipo non sono associati. Il bytecode prodotto, quindi, contiene solo classi, interfacce e metodi ordinari. Inserire cast di tipo se necessario per preservare la sicurezza del tipo. Generare metodi bridge per preservare il polimorfismo in tipi generici estesi. La cancellazione del tipo garantisce che non vengano create nuove classi per i tipi con parametri; di conseguenza, i generici non comportano costi generali di runtime.

http://docs.oracle.com/javase/tutorial/java/generics/erasure.html


Jup, è impossibile. Java avrebbe bisogno di generici reificati per farlo funzionare.
Henning

15

La tecnica descritta in questo articolo da Ian Robertson funziona per me.

In breve esempio veloce e sporco:

 public abstract class AbstractDAO<T extends EntityInterface, U extends QueryCriteria, V>
 {
    /**
     * Method returns class implementing EntityInterface which was used in class
     * extending AbstractDAO
     *
     * @return Class<T extends EntityInterface>
     */
    public Class<T> returnedClass()
    {
        return (Class<T>) getTypeArguments(AbstractDAO.class, getClass()).get(0);
    }

    /**
     * Get the underlying class for a type, or null if the type is a variable
     * type.
     *
     * @param type the type
     * @return the underlying class
     */
    public static Class<?> getClass(Type type)
    {
        if (type instanceof Class) {
            return (Class) type;
        } else if (type instanceof ParameterizedType) {
            return getClass(((ParameterizedType) type).getRawType());
        } else if (type instanceof GenericArrayType) {
            Type componentType = ((GenericArrayType) type).getGenericComponentType();
            Class<?> componentClass = getClass(componentType);
            if (componentClass != null) {
                return Array.newInstance(componentClass, 0).getClass();
            } else {
                return null;
            }
        } else {
            return null;
        }
    }

    /**
     * Get the actual type arguments a child class has used to extend a generic
     * base class.
     *
     * @param baseClass the base class
     * @param childClass the child class
     * @return a list of the raw classes for the actual type arguments.
     */
    public static <T> List<Class<?>> getTypeArguments(
            Class<T> baseClass, Class<? extends T> childClass)
    {
        Map<Type, Type> resolvedTypes = new HashMap<Type, Type>();
        Type type = childClass;
        // start walking up the inheritance hierarchy until we hit baseClass
        while (!getClass(type).equals(baseClass)) {
            if (type instanceof Class) {
                // there is no useful information for us in raw types, so just keep going.
                type = ((Class) type).getGenericSuperclass();
            } else {
                ParameterizedType parameterizedType = (ParameterizedType) type;
                Class<?> rawType = (Class) parameterizedType.getRawType();

                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                TypeVariable<?>[] typeParameters = rawType.getTypeParameters();
                for (int i = 0; i < actualTypeArguments.length; i++) {
                    resolvedTypes.put(typeParameters[i], actualTypeArguments[i]);
                }

                if (!rawType.equals(baseClass)) {
                    type = rawType.getGenericSuperclass();
                }
            }
        }

        // finally, for each actual type argument provided to baseClass, determine (if possible)
        // the raw class for that type argument.
        Type[] actualTypeArguments;
        if (type instanceof Class) {
            actualTypeArguments = ((Class) type).getTypeParameters();
        } else {
            actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
        }
        List<Class<?>> typeArgumentsAsClasses = new ArrayList<Class<?>>();
        // resolve types by chasing down type variables.
        for (Type baseType : actualTypeArguments) {
            while (resolvedTypes.containsKey(baseType)) {
                baseType = resolvedTypes.get(baseType);
            }
            typeArgumentsAsClasses.add(getClass(baseType));
        }
        return typeArgumentsAsClasses;
    }
  }

3
In quale riga specifica di questo codice vengono letti i parametri del tipo di runtime?
Ivan Matavulj,

Qui? Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
Ondrej Bozek,

9

Penso che ci sia un'altra soluzione elegante.

Quello che vuoi fare è "passare" in modo sicuro il tipo del parametro di tipo generico dalla classe concerete alla superclasse.

Se ti permetti di pensare al tipo di classe come "metadati" sulla classe, ciò suggerisce il metodo Java per codificare i metadati in fase di esecuzione: annotazioni.

Per prima cosa definisci un'annotazione personalizzata seguendo queste linee:

import java.lang.annotation.*;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EntityAnnotation {
    Class entityClass();
}

È quindi possibile aggiungere l'annotazione alla sottoclasse.

@EntityAnnotation(entityClass =  PassedGenericType.class)
public class Subclass<PassedGenericType> {...}

Quindi puoi usare questo codice per ottenere il tipo di classe nella tua classe base:

import org.springframework.core.annotation.AnnotationUtils;
.
.
.

private Class getGenericParameterType() {
    final Class aClass = this.getClass();
    EntityAnnotation ne = 
         AnnotationUtils.findAnnotation(aClass, EntityAnnotation.class);

    return ne.entityClass();
}

Alcune limitazioni di questo approccio sono:

  1. Specificare il tipo generico ( PassedGenericType) in DUE posizioni anziché in uno non DRY.
  2. Ciò è possibile solo se è possibile modificare le sottoclassi concrete.

Sì, non è ASCIUTTO, tuttavia è più pulito dell'approccio di estensione suggerito sopra. Mi è piaciuto. grazie
agodinhost l'

8

Questa è la mia soluzione:

import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;

public class GenericClass<T extends String> {

  public static void main(String[] args) {
     for (TypeVariable typeParam : GenericClass.class.getTypeParameters()) {
      System.out.println(typeParam.getName());
      for (Type bound : typeParam.getBounds()) {
         System.out.println(bound);
      }
    }
  }
}

10
Questa non è una risposta a questa domanda.
Adam Arold,

1
Il mio codice non è la soluzione esatta per la domanda. Restituisce i parametri di tipo generico della classe, ma non il tipo effettivo di T. Ma può essere utile per gli altri che si imbattono nella domanda e stanno cercando la mia soluzione.
Matthias M,

1
getClass (). getGenericSuperclass () otterrà lo stesso effetto.
Gorkij,

5

Ecco la soluzione funzionante !!!

@SuppressWarnings("unchecked")
    private Class<T> getGenericTypeClass() {
        try {
            String className = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0].getTypeName();
            Class<?> clazz = Class.forName(className);
            return (Class<T>) clazz;
        } catch (Exception e) {
            throw new IllegalStateException("Class is not parametrized with generic type!!! Please use extends <> ");
        }
    } 

NOTE: Può essere usato solo come superclasse
1. Deve essere esteso con la classe digitata ( Child extends Generic<Integer>)
OPPURE

2. Deve essere creato come implementazione anonima ( new Generic<Integer>() {};)


4

Non puoi. Se aggiungi una variabile membro di tipo T alla classe (non devi nemmeno inizializzarla), puoi usarla per recuperare il tipo.


2
Oops, ok. Si fa necessario inizializzare da un costruttore.
andrewmu,

4

Ecco un modo, che ho dovuto usare una o due volte:

public abstract class GenericClass<T>{
    public abstract Class<T> getMyType();
}

Insieme a

public class SpecificClass extends GenericClass<String>{

    @Override
    public Class<String> getMyType(){
        return String.class;
    }
}

4
Funziona tecnicamente, tuttavia non risolve il caso generale, e penso che sia quello che segue il poster originale.
ggb667,

Questo non merita di essere votato come è successo - il poster originale non è stato esplicito. Questa risposta offre un modello di progettazione che fa il lavoro ed è facile da implementare, a condizione è adatto per rendere la classe generica astratta.
VirtualMichael,

4

Una soluzione semplice per questa cabina è come di seguito

public class GenericDemo<T>{
    private T type;

    GenericDemo(T t)
    {
        this.type = t;
    }

    public String getType()
    {
        return this.type.getClass().getName();
    }

    public static void main(String[] args)
    {
        GenericDemo<Integer> obj = new  GenericDemo<Integer>(5);
        System.out.println("Type: "+ obj.getType());
    }
}

3

Per completare alcune delle risposte qui, ho dovuto ottenere il ParametrizedType di MyGenericClass, non importa quanto sia alta la gerarchia, con l'aiuto della ricorsione:

private Class<T> getGenericTypeClass() {
        return (Class<T>) (getParametrizedType(getClass())).getActualTypeArguments()[0];
}

private static ParameterizedType getParametrizedType(Class clazz){
    if(clazz.getSuperclass().equals(MyGenericClass.class)){ // check that we are at the top of the hierarchy
        return (ParameterizedType) clazz.getGenericSuperclass();
    } else {
        return getParametrizedType(clazz.getSuperclass());
    }
}

1

Ecco il mio trucco:

public class Main {

    public static void main(String[] args) throws Exception {

        System.out.println(Main.<String> getClazz());

    }

    static <T> Class getClazz(T... param) {

        return param.getClass().getComponentType();
    }

}

1
Nota: questo non funziona quando Tè una variabile di tipo. Nel caso in cui si Ttratti di una variabile di tipo, varargs crea una matrice di cancellazione di T. Vedi ad esempio http://ideone.com/DIPNwd .
Radiodef,

1
Questo restituisce "Object"
yahya

Forse stai cercando di rispondere ad un'altra domanda 🤔
Khan,

1

Nel caso in cui usi memorizzare una variabile usando il tipo generico puoi facilmente risolvere questo problema aggiungendo un metodo getClassType come segue:

public class Constant<T> {
  private T value;

  @SuppressWarnings("unchecked")
  public Class<T> getClassType () {
    return ((Class<T>) value.getClass());
  }
}

In seguito utilizzo l'oggetto di classe fornito per verificare se si tratta di un'istanza di una determinata classe, come segue:

Constant<?> constant = ...;
if (constant.getClassType().equals(Integer.class)) {
    Constant<Integer> integerConstant = (Constant<Integer>)constant;
    Integer value = integerConstant.getValue();
    // ...
}

2
Questo è problematico, sfortunatamente. Prima di tutto, cosa succede se lo valueè null? In secondo luogo, cosa succede se si valuetratta di una sottoclasse di T? Constant<Number> c = new Constant<Number>(new Integer(0)); Class<Number> n = c.getClassType();ritorna Integer.classquando dovrebbe tornare Number.class. Sarebbe più corretto tornare Class<? extends T>. Integer.class è un Class<? extends Number> ma non un Class<Number>.
Radiodef,

1

Ecco la mia soluzione

public class GenericClass<T>
{
    private Class<T> realType;

    public GenericClass() {
        findTypeArguments(getClass());
    }

    private void findTypeArguments(Type t) {
        if (t instanceof ParameterizedType) {
            Type[] typeArgs = ((ParameterizedType) t).getActualTypeArguments();
            realType = (Class<T>) typeArgs[0];
        } else {
            Class c = (Class) t;
            findTypeArguments(c.getGenericSuperclass());
        }
    }

    public Type getMyType()
    {
        // How do I return the type of T? (your question)
        return realType;
    }
}

Indipendentemente dal livello della gerarchia di classi, questa soluzione funziona ancora, ad esempio:

public class FirstLevelChild<T> extends GenericClass<T> {

}

public class SecondLevelChild extends FirstLevelChild<String> {

}

In questo caso, getMyType () = java.lang.String


Questo non sta restituendo il tipo di T. Sta restituendo T non java.lang.String oltre al codice non riesce a convertire il tipo in classe <T>
Mohy Eldeen

Ecco un campione online che ho realizzato. Fai clic su Compila ed esegui, quindi puoi ottenere il risultato. tutorialspoint.com/…
Lin Yu Cheng,

Funziona per me - quando WildFly Weld CDI ha rotto un metodo alternativo.
Andre,

Ho ricevutoException in thread "main" java.lang.NullPointerException at Main$ClassA.findTypeArguments(Main.java:54) at Main$ClassA.findTypeArguments(Main.java:54) at Main$ClassA.findTypeArguments(Main.java:54) at Main$ClassA.<init>(Main.java:43) at Main.main(Main.java:61)
yuyang il

0
public static final Class<?> getGenericArgument(final Class<?> clazz)
{
    return (Class<?>) ((ParameterizedType) clazz.getGenericSuperclass()).getActualTypeArguments()[0];
}

0

Ecco la mia soluzione Gli esempi dovrebbero spiegarlo. L'unico requisito è che una sottoclasse deve impostare il tipo generico, non un oggetto.

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.HashMap;
import java.util.Map;

public class TypeUtils {

    /*** EXAMPLES ***/

    public static class Class1<A, B, C> {

        public A someA;
        public B someB;
        public C someC;

        public Class<?> getAType() {
            return getTypeParameterType(this.getClass(), Class1.class, 0);
        }

        public Class<?> getCType() {
            return getTypeParameterType(this.getClass(), Class1.class, 2);
        }
    }

    public static class Class2<D, A, B, E, C> extends Class1<A, B, C> {

        public B someB;
        public D someD;
        public E someE;
    }

    public static class Class3<E, C> extends Class2<String, Integer, Double, E, C> {

        public E someE;
    }

    public static class Class4 extends Class3<Boolean, Long> {

    }

    public static void test() throws NoSuchFieldException {

        Class4 class4 = new Class4();
        Class<?> typeA = class4.getAType(); // typeA = Integer
        Class<?> typeC = class4.getCType(); // typeC = Long

        Field fieldSomeA = class4.getClass().getField("someA");
        Class<?> typeSomeA = TypeUtils.getFieldType(class4.getClass(), fieldSomeA); // typeSomeA = Integer

        Field fieldSomeE = class4.getClass().getField("someE");
        Class<?> typeSomeE = TypeUtils.getFieldType(class4.getClass(), fieldSomeE); // typeSomeE = Boolean


    }

    /*** UTILS ***/

    public static Class<?> getTypeVariableType(Class<?> subClass, TypeVariable<?> typeVariable) {
        Map<TypeVariable<?>, Type> subMap = new HashMap<>();
        Class<?> superClass;
        while ((superClass = subClass.getSuperclass()) != null) {

            Map<TypeVariable<?>, Type> superMap = new HashMap<>();
            Type superGeneric = subClass.getGenericSuperclass();
            if (superGeneric instanceof ParameterizedType) {

                TypeVariable<?>[] typeParams = superClass.getTypeParameters();
                Type[] actualTypeArgs = ((ParameterizedType) superGeneric).getActualTypeArguments();

                for (int i = 0; i < typeParams.length; i++) {
                    Type actualType = actualTypeArgs[i];
                    if (actualType instanceof TypeVariable) {
                        actualType = subMap.get(actualType);
                    }
                    if (typeVariable == typeParams[i]) return (Class<?>) actualType;
                    superMap.put(typeParams[i], actualType);
                }
            }
            subClass = superClass;
            subMap = superMap;
        }
        return null;
    }

    public static Class<?> getTypeParameterType(Class<?> subClass, Class<?> superClass, int typeParameterIndex) {
        return TypeUtils.getTypeVariableType(subClass, superClass.getTypeParameters()[typeParameterIndex]);
    }

    public static Class<?> getFieldType(Class<?> clazz, AccessibleObject element) {
        Class<?> type = null;
        Type genericType = null;

        if (element instanceof Field) {
            type = ((Field) element).getType();
            genericType = ((Field) element).getGenericType();
        } else if (element instanceof Method) {
            type = ((Method) element).getReturnType();
            genericType = ((Method) element).getGenericReturnType();
        }

        if (genericType instanceof TypeVariable) {
            Class<?> typeVariableType = TypeUtils.getTypeVariableType(clazz, (TypeVariable) genericType);
            if (typeVariableType != null) {
                type = typeVariableType;
            }
        }

        return type;
    }

}

-1

Ho fatto lo stesso di @Moesio Above ma a Kotlin si poteva fare così:

class A<T : SomeClass>() {

    var someClassType : T

    init(){
    this.someClassType = (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments[0] as Class<T>
    }

}

-4

Potrebbe essere utile a qualcuno. Puoi usare java.lang.ref.WeakReference; per di qua:

class SomeClass<N>{
  WeakReference<N> variableToGetTypeFrom;

  N getType(){
    return variableToGetTypeFrom.get();
  }
}

Come dovrebbe essere usata questa classe? Perché WeakReference? Fornisci alcune spiegazioni con la tua risposta, non solo un codice.
Abhijit Sarkar,

Quindi se ne hai uno SomeClass<MyClass> puoi creare un'istanza SomeClasse chiamare getTypequell'istanza e avere il tempo di esecuzione MyClass.
TheLetch,

Certo, ma perché WeakReference? Quello che hai detto non è diverso dalla maggior parte delle altre risposte.
Abhijit Sarkar,

In primo luogo il mio approccio è più breve (meno codice), in secondo luogo i riferimenti deboli non impediscono che i loro riferimenti vengano resi finalizzabili, e per quanto ne so non usa la riflessione, quindi è veloce
TheLetch,

Questo non ottiene il tipo di nulla, questo restituisce un oggetto di questo tipo, che, cronaca, si può fare con letteralmente qualsiasi tipo di involucro ( AtomicReference, List, Set).
Frontear,

-5

Ho trovato che questa è una soluzione comprensibile e facilmente spiegabile

public class GenericClass<T> {

    private Class classForT(T...t) {
        return t.getClass().getComponentType();
    }

    public static void main(String[] args) {
        GenericClass<String> g = new GenericClass<String>();

        System.out.println(g.classForT());
        System.out.println(String.class);
    }
}

Spiega (T...t). (Ecco perché questo codice non funziona.)
Christopher Schneider,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.