Obtenir un type de classe générique à l'exécution

508

Comment puis-je atteindre cet objectif?

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

Tout ce que j'ai essayé jusqu'à présent renvoie toujours le type Objectplutôt que le type spécifique utilisé.

Glenn
la source
3
@SamuelVidal Ce n'est pas .NET, c'est Java.
Frontear

Réponses:

317

Comme d'autres l'ont mentionné, cela n'est possible que par réflexion dans certaines circonstances.

Si vous avez vraiment besoin du type, voici le modèle de contournement habituel (sans danger pour le type):

public class GenericClass<T> {

     private final Class<T> type;

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

     public Class<T> getMyType() {
         return this.type;
     }
}
Henning
la source
58
J'aime cette réponse mais c'est un peu lourd à instancier: GenericClass <AnotherClass> g = new GenericClass <AnotherClass> (AnotherClass.class);
Eliseo Ocampos
1
C'est encore plus verbeux si vous utilisez une approche dao / factory / manager. Foo foo1 = GetDao<Foo>(Foo.class).get(Foo.class, 1)
djmj
2
C'est vrai, mais ne fonctionne pas dans tous les cas comme les beans distants sans état qui sont instanciés par conteneur / réflexion.
djmj
6
Pour faire suite à mon commentaire précédent - après beaucoup de douleur à jouer avec la réflexion, j'ai fini par utiliser cette réponse.
Tomáš Zato - Réintègre Monica
18
Vous pouvez contourner la référence superflue en fournissant une méthode d'usine statique générique. Quelque chose comme public static <T> GenericClass<T> of(Class<T> type) {...}puis l' appeler comme tel: GenericClass<String> var = GenericClass.of(String.class). Un peu mieux.
Joeri Hendrickx
263

J'ai vu quelque chose comme ça

private Class<T> persistentClass;

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

dans l' exemple hibernate GenericDataAccessObjects

FrVaBe
la source
84
Cette technique fonctionne lorsque le paramètre de type est défini sur la superclasse immédiate, mais elle échoue si le paramètre de type est défini ailleurs dans la hiérarchie de types. Pour gérer des cas plus complexes, quelque chose comme TypeTools peut être utilisé. Les documents incluent un exemple de DAO générique plus sophistiqué.
Jonathan
25
Cela ne renvoie que les paramètres de type réels utilisés quand une classe implémente / étend quelque chose qui a des déclarations génériques, elle ne retourne pas les paramètres de type réels utilisés quand une INSTANCE est instanciée. En d'autres termes, il PEUT dire que dans class A implements Comparable<String>, le paramètre de type réel est String, mais il NE PEUT PAS dire que dans Set<String> a = new TreeSet<String>(), le paramètre de type réel est String. En fait, les informations sur les paramètres de type sont "effacées" après la compilation, comme expliqué dans d'autres réponses.
Siu Ching Pong -Asuka Kenji-
32
Je reçois java.lang.Class cannot be cast to java.lang.reflect.ParameterizedTypepour cette réponse.
Tomáš Zato - Réintègre Monica
4
Cette approche peut également être réalisée en utilisant Class-Mateles gens de Jackson. J'ai écrit un résumé
yunspace
5
@ TomášZato L'appel simple du code ci-dessus m'a renvoyé la même exception. Je sais qu'il est un peu tard, mais de toute façon, dans mon cas, j'ai dû appeler (Class<T>) ((ParameterizedType)getClass().getSuperclass().getGenericSuperclass()).getActualTypeArguments()pour obtenir des arguments de type réels.
AndrewMcCoist
99

Les génériques ne sont pas réifiés au moment de l'exécution. Cela signifie que les informations ne sont pas présentes au moment de l'exécution.

Ajouter des génériques à Java tout en conservant la compatibilité descendante était un tour de force (vous pouvez voir le document fondateur à ce sujet: Rendre l'avenir sûr pour le passé: ajouter de la généricité au langage de programmation Java ).

Il existe une riche littérature sur le sujet, et certaines personnes ne sont pas satisfaites de l'état actuel, certaines disent qu'en fait c'est un leurre et qu'il n'y en a pas vraiment besoin. Vous pouvez lire les deux liens, je les ai trouvés assez intéressants.

ewernli
la source
179
Bien sûr, nous ne sommes pas satisfaits, .NET a un mécanisme de gestion générique bien meilleur
Pacerier
6
@Pacerier: mais les génériques réifiés à eux seuls n'amèneraient pas Java au niveau de .NET. La valeur saisit un code spécialisé pour ceux-ci est au moins tout aussi importante pour expliquer pourquoi .NET est meilleur dans le domaine des génériques.
Joachim Sauer
@JoachimSauer, oui types de valeurs. J'avais toujours voulu ceux en java. Que voulez-vous dire par code spécialisé?
Pacerier
3
@ spaaarky21 Non, les paramètres de type génériques sont supprimés lors de la compilation (soi-disant "effacement", vous pouvez le google). L'astuce dans la réponse de FrVaBe ne fonctionne que si les paramètres de type de la superclasse sont connus statiquement (voir le premier commentaire de Johnathn)
ewernli
1
L'effacement de type Java est un défaut de conception historique; plus de code a été écrit pour le contourner que ce qui a été écrit pour l'implémenter.
Abhijit Sarkar
68

Utilisez la goyave.

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

Un retour while, j'ai posté quelques exemples Fledge complet , y compris les classes abstraites et les sous - classes ici .

Remarque: cela nécessite que vous instanciez une sous - classe de GenericClassafin qu'elle puisse lier correctement le paramètre type. Sinon, il retournera simplement le type comme T.

Cody A. Ray
la source
3
Notez que je crée une sous-classe anonyme vide (voir les deux accolades à la fin). Cela utilise la réflexion pour lutter contre l'effacement du type d'exécution de Java. Vous pouvez en savoir plus ici: code.google.com/p/guava-libraries/wiki/ReflectionExplained
Cody A. Ray
3
@ CodyA.Ray Votre code lance a java.lang.IllegalArgumentException: class com.google.common.reflect.TypeToken isn't parameterized. J'ai donc changé la ligne new TypeToken(getClass()) { }en new TypeToken<T>(getClass()) { }. Maintenant, le code fonctionne bien, mais le type est toujours «T». Voir ceci: gist.github.com/m-manu/9cda9d8f9d53bead2035
Manu Manjunath
3
@Dominik Veuillez voir l'exemple mis à jour que vous pouvez copier et coller pour vous tester. J'ai également ajouté une note précisant que vous devez instancier une sous-classe (comme indiqué). Comme conseil d'étiquette générale, veuillez lire les articles liés et les javadocs associés avant d'accuser une affiche de "vœux pieux". J'ai utilisé plusieurs fois un code de production similaire. Les assistants de goyave que je présente sont destinés à ce cas d'utilisation exact et leurs javadocs montrent presque une réponse exacte à cette question. docs.guava-libraries.googlecode.com/git/javadoc/com/google/…
Cody A. Ray
1
Cette réponse fonctionne très bien avec Java8 - J'ai une interface et une classe d'usine pour émettre différents types, et je voulais que les utilisateurs puissent facilement déterminer le type. Dans mon interface, j'ai ajouté la méthode par défaut: default Type getParameterType() { final TypeToken<T> typeToken = new TypeToken<T>(getClass()) {}; final Type type = typeToken.getType(); return type; }
Richard Sand
2
@ CodyA.Ray Comme cela ne fonctionne qu'avec les sous-classes de GenericClass, vous devez rendre cette classe abstractafin qu'une mauvaise utilisation ne se compile pas.
Martin
37

Sûr que vous pouvez.

Java n'utilise l'information au moment de l' exécution, pour des raisons de compatibilité ascendante. Mais l'information est effectivement présente sous forme de métadonnées et sont accessibles via la réflexion (mais elles ne sont toujours pas utilisées pour la vérification de type).

Depuis l'API officielle:

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

Cependant , pour votre scénario, je n'utiliserais pas la réflexion. Personnellement, je suis plus enclin à l'utiliser pour le code cadre. Dans votre cas, je voudrais simplement ajouter le type comme paramètre constructeur.

Joeri Hendrickx
la source
10
getActualTypeArguments ne renvoie que les arguments de type pour la classe immédiate. Si vous avez une hiérarchie de type complexe où T pourrait être paramétré n'importe où dans la hiérarchie, vous devrez faire un peu de travail pour comprendre de quoi il s'agit. C'est plus ou moins ce que fait TypeTools .
Jonathan
36

Les génériques Java sont principalement au moment de la compilation, ce qui signifie que les informations de type sont perdues lors de l'exécution.

class GenericCls<T>
{
    T t;
}

sera compilé en quelque chose comme

class GenericCls
{
   Object o;
}

Pour obtenir les informations de type lors de l'exécution, vous devez les ajouter comme argument du ctor.

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

Exemple:

GenericCls<?> instance = new GenericCls<String>(String.class);
assert instance.getType() == String.class;
josefx
la source
9
private final Class<T> type;
naXa
Comment puis-je créer un type de tableau à partir de celui-ci:Type t = //String[]
Pawel Cioch
@PawelCioch java.lang.reflect.Array.newInstance (type d'élément, longueur); espérons que cela aide (javadoc peut être trouvé ici docs.oracle.com/javase/8/docs/api/java/lang/reflect/… )
josefx
@PawelCioch a manqué un .getClass () pour obtenir le type du tableau créé. Il ne semble pas y avoir de moyen direct d'obtenir une classe de tableau. La plupart des collections Java utilisent simplement Object [] à la place.
josefx
23
public abstract class AbstractDao<T>
{
    private final Class<T> persistentClass;

    public AbstractDao()
    {
        this.persistentClass = (Class<T>) ((ParameterizedType) this.getClass().getGenericSuperclass())
                .getActualTypeArguments()[0];
    }
}
Ali Yeganeh
la source
3
Je vote pour cette réponse parce que c'est une solution qui fonctionne pour la question posée. Cependant, pour ceux qui veulent naviguer dans la hiérarchie des classes comme moi avec plus d'une classe générique, cela ne fonctionnera pas. Parce que vous obtiendrez java.lang.object au lieu de la classe réelle.
KMC
3
Veuillez noter que cette solution fonctionne UNIQUEMENT si la classe qui contient le type générique est ABSTRACT
BabaNew
N'a pas fonctionné avec ma classe abstraite en Java 11
JRA_TLL
@JRA_TLL vous avez apparemment fait quelque chose de mal. Je viens de l'utiliser avec Java 12 et fonctionne comme un charme.
Googie
Si vous souhaitez remonter dans la hiérarchie des vues, vous pouvez convertir la genericSuperclass en classe <*> et obtenir la genericSuperclass. De préférence en boucle.
fupduck
21

J'ai utilisé l'approche suivante:

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();
    }
}
Moesio
la source
9
J'ai obtenu "Exception in thread" main "java.lang.ClassCastException: java.lang.Class ne peut pas être converti en java.lang.reflect.ParameterizedType" avec cet exemple de code
yuyang
16

Je ne pense pas que vous puissiez le faire, Java utilise l'effacement des types lors de la compilation afin que votre code soit compatible avec les applications et les bibliothèques qui ont été créées pré-génériques.

Depuis Oracle Docs:

Type d'effacement

Les génériques ont été introduits dans le langage Java pour fournir des vérifications de type plus strictes au moment de la compilation et pour prendre en charge la programmation générique. Pour implémenter des génériques, le compilateur Java applique l'effacement de type à:

Remplacez tous les paramètres de type dans les types génériques par leurs limites ou Object si les paramètres de type ne sont pas limités. Le bytecode produit, par conséquent, ne contient que des classes, des interfaces et des méthodes ordinaires. Insérez des moulages de type si nécessaire pour préserver la sécurité du type. Générez des méthodes de pont pour préserver le polymorphisme dans les types génériques étendus. L'effacement de type garantit qu'aucune nouvelle classe n'est créée pour les types paramétrés; par conséquent, les génériques n'encourent aucun temps d'exécution.

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

Dimitar
la source
Jup, c'est impossible. Java aurait besoin de génériques réifiés pour que cela fonctionne.
Henning
15

Technique décrite dans cet article par Ian Robertson fonctionne pour moi.

En bref exemple rapide et sale:

 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;
    }
  }
Ondrej Bozek
la source
3
Sur quelle ligne spécifique de ce code les paramètres de type d'exécution sont-ils en cours de lecture?
Ivan Matavulj
ici? Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
Ondrej Bozek
9

Je pense qu'il existe une autre solution élégante.

Ce que vous voulez faire, c'est (en toute sécurité) "passer" le type du paramètre de type générique de la classe concerete à la superclasse.

Si vous vous autorisez à considérer le type de classe comme des "métadonnées" sur la classe, cela suggère la méthode Java pour encoder les métadonnées à l'exécution: les annotations.

Définissez d'abord une annotation personnalisée le long de ces lignes:

import java.lang.annotation.*;

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

Vous pouvez alors devoir ajouter l'annotation à votre sous-classe.

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

Ensuite, vous pouvez utiliser ce code pour obtenir le type de classe dans votre classe de 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();
}

Certaines limites de cette approche sont:

  1. Vous spécifiez le type générique (PassedGenericType ) à DEUX endroits plutôt que celui qui n'est pas SEC.
  2. Cela n'est possible que si vous pouvez modifier les sous-classes concrètes.
DaBlick
la source
Oui, ce n'est pas SEC, mais c'est plus propre que l'approche d'extension suggérée ci-dessus. Je l'ai aimé. merci
agodinhost
8

Voici ma solution:

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);
      }
    }
  }
}
Matthias M
la source
10
Ce n'est pas une réponse à cette question.
Adam Arold
1
Mon code n'est pas la solution exacte à la question. Il renvoie les paramètres de type génériques de la classe, mais pas le type réel de T. Mais il peut être utile pour ceux qui trébuchent sur la question et recherchent ma solution.
Matthias M,
1
getClass (). getGenericSuperclass () obtiendra le même effet.
Gorky
5

Voici une solution de travail !!!

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

REMARQUES: Peut être utilisé uniquement en tant que superclasse
1. Doit être étendu avec la classe typée ( Child extends Generic<Integer>)
OU

2. Doit être créé en tant qu'implémentation anonyme ( new Generic<Integer>() {};)

Yaroslav Kovbas
la source
4

Tu ne peux pas. Si vous ajoutez une variable membre de type T à la classe (vous n'avez même pas besoin de l'initialiser), vous pouvez l'utiliser pour récupérer le type.

andrewmu
la source
2
Oups, d'accord. Vous ne devez initialiser à partir d' un constructeur.
andrewmu
4

Voici une façon, que j'ai dû utiliser une ou deux fois:

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

De même que

public class SpecificClass extends GenericClass<String>{

    @Override
    public Class<String> getMyType(){
        return String.class;
    }
}
PJWeisberg
la source
4
Cela fonctionne techniquement, mais cela ne résout pas le cas général, et je pense que c'est ce que l'affiche originale est après.
ggb667
Cela ne mérite pas d'être rejeté comme c'est le cas - l'affiche originale n'a pas été explicite. Cette réponse offre un motif de conception qui fait le travail et est facile à mettre en œuvre, à condition qu'il convient de faire la classe générique abstraite.
VirtualMichael
4

Une solution simple pour cette cabine comme ci-dessous

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());
    }
}
Paul92
la source
3

Pour compléter certaines des réponses ici, j'ai dû obtenir le ParametrizedType de MyGenericClass, quelle que soit la hauteur de la hiérarchie, à l'aide de la récursivité:

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());
    }
}
galex
la source
1

Voici mon astuce:

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

}
HRgiger
la source
1
Remarque: cela ne fonctionne pas lorsqu'il Ts'agit d'une variable de type. Dans le cas où il Ts'agit d'une variable de type, le varargs crée un tableau d'effacement de T. Voir par exemple http://ideone.com/DIPNwd .
Radiodef
1
Cela renvoie "Object"
yahya
Peut-être que vous essayez de répondre à une autre question
Khan
1

Juste au cas où vous utilisez stocker une variable en utilisant le type générique, vous pouvez facilement résoudre ce problème en ajoutant une méthode getClassType comme suit:

public class Constant<T> {
  private T value;

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

J'utilise l'objet de classe fourni plus tard pour vérifier s'il s'agit d'une instance d'une classe donnée, comme suit:

Constant<?> constant = ...;
if (constant.getClassType().equals(Integer.class)) {
    Constant<Integer> integerConstant = (Constant<Integer>)constant;
    Integer value = integerConstant.getValue();
    // ...
}
Pablo Trinidad
la source
2
C'est problématique, malheureusement. Tout d' abord, si valueest null? Deuxièmement, que faire si valueest une sous-classe de T? Constant<Number> c = new Constant<Number>(new Integer(0)); Class<Number> n = c.getClassType();retourne Integer.classquand il devrait revenir Number.class. Il serait plus correct de revenir Class<? extends T>. Integer.class est un Class<? extends Number> mais pas un Class<Number>.
Radiodef
1

Voici ma solution

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

Quel que soit le niveau de votre hiérarchie de classes, cette solution fonctionne toujours, par exemple:

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

}

public class SecondLevelChild extends FirstLevelChild<String> {

}

Dans ce cas, getMyType () = java.lang.String

Lin Yu Cheng
la source
Cela ne renvoie pas le type de T. Il renvoie T non java.lang.String outre le code ne parvient pas à convertir le type en classe <T>
Mohy Eldeen
Voici un échantillon en ligne que j'ai réalisé. Cliquez sur compiler et exécuter, vous pourrez alors obtenir le résultat. tutorialspoint.com/…
Lin Yu Cheng
Fonctionne pour moi - quand WildFly Weld CDI a cassé une méthode alternative.
Andre
Je suis arrivéException 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
0
public static final Class<?> getGenericArgument(final Class<?> clazz)
{
    return (Class<?>) ((ParameterizedType) clazz.getGenericSuperclass()).getActualTypeArguments()[0];
}
kayz1
la source
0

Voici ma solution. Les exemples devraient l'expliquer. La seule exigence est qu'une sous-classe doit définir le type générique, pas un objet.

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

}
PetermaxX
la source
-1

J'ai fait la même chose que @Moesio Above mais dans Kotlin, cela pourrait être fait de cette façon:

class A<T : SomeClass>() {

    var someClassType : T

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

}
Bonestack
la source
-4

Cela pourrait être utile à quelqu'un. Vous pouvez utiliser java.lang.ref.WeakReference; par ici:

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

  N getType(){
    return variableToGetTypeFrom.get();
  }
}
TheLetch
la source
Comment cette classe est-elle censée être utilisée? Pourquoi WeakReference? Veuillez fournir quelques explications avec votre réponse, pas seulement du code.
Abhijit Sarkar
Donc, si vous en avez un, SomeClass<MyClass> vous pouvez instancier SomeClasset appeler getTypecette instance et avoir le temps d'exécution MyClass.
TheLetch
Bien sûr, mais pourquoi WeakReference? Ce que vous avez dit n'est pas différent de la plupart des autres réponses.
Abhijit Sarkar
Premièrement, mon approche est plus courte (moins de code), deuxièmement, les références faibles n'empêchent pas leurs référents de devenir finalisables, et pour autant que je sache, elles n'utilisent pas la réflexion, donc c'est rapide
TheLetch
Cela ne va pas le type de quoi que ce soit, cela retourne un objet de ce type, qui, FYI, vous pouvez faire avec littéralement tout type d'emballage ( AtomicReference, List, Set).
Frontear
-5

J'ai trouvé que c'était une solution simple, compréhensible et facilement explicable

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);
    }
}
Mark Karchner
la source
Expliquez (T...t). (C'est pourquoi ce code ne fonctionne pas.)
Christopher Schneider