Comment obtenir une instance de classe de type générique T

J’ai une classe de génériques, Foo . Dans une méthode de Foo , je veux obtenir l’instance de classe de type T, mais je ne peux simplement pas appeler T.class .

Quelle est la meilleure façon de contourner le T.class utilisant T.class ?

    La réponse courte est qu’il n’y a aucun moyen de trouver le type d’exécution des parameters de type génériques en Java. Je suggère de lire le chapitre sur l’effacement de type dans le tutoriel Java pour plus de détails.

    Une solution populaire consiste à passer la Class du paramètre type dans le constructeur du type générique, par exemple

     class Foo { final Class typeParameterClass; public Foo(Class typeParameterClass) { this.typeParameterClass = typeParameterClass; } public void bar() { // you can access the typeParameterClass here and do whatever you like } } 

    Je cherchais un moyen de le faire moi-même sans append une dépendance supplémentaire au classpath. Après quelques recherches, j’ai trouvé que c’était possible tant que vous possédiez un supertype générique. Cela me convenait car je travaillais avec une couche DAO avec un super-type de couche générique. Si cela correspond à votre scénario, c’est l’approche la plus appropriée à mon humble avis.

    La plupart des cas d’utilisation de génériques rencontrés ont une sorte de supertype générique, par exemple List pour ArrayList ou GenericDAO pour DAO , etc.

    Solution Java pure

    L’article sur l’ access aux types génériques à l’exécution de Java explique comment procéder en utilisant Java pur.

    Solution de spring

    Mon projet utilisait Spring, ce qui est encore mieux car Spring a une méthode utilitaire pratique pour trouver le type. C’est la meilleure approche pour moi car elle est la plus propre. Je suppose que si vous n’utilisiez pas Spring, vous pourriez écrire votre propre méthode utilitaire.

     import org.springframework.core.GenericTypeResolver; public abstract class AbstractHibernateDao implements DataAccessObject { @Autowired private SessionFactory sessionFactory; private final Class genericType; private final Ssortingng RECORD_COUNT_HQL; private final Ssortingng FIND_ALL_HQL; @SuppressWarnings("unchecked") public AbstractHibernateDao() { this.genericType = (Class) GenericTypeResolver.resolveTypeArgument(getClass(), AbstractHibernateDao.class); this.RECORD_COUNT_HQL = "select count(*) from " + this.genericType.getName(); this.FIND_ALL_HQL = "from " + this.genericType.getName() + " t "; } 

    Il y a cependant une petite faille: si vous définissez votre classe Foo comme abstraite. Cela signifie que vous devez instancier votre classe comme suit:

     Foo myFoo = new Foo(){}; 

    (Notez les doubles accolades à la fin.)

    Vous pouvez maintenant récupérer le type de T à l’exécution:

     Type mySuperclass = myFoo.getClass().getGenericSuperclass(); Type tType = ((ParameterizedType)mySuperclass).getActualTypeArguments()[0]; 

    Notez cependant que mySuperclass doit être la super-classe de la définition de classe définissant réellement le type final pour T

    Ce n’est pas non plus très élégant, mais vous devez décider si vous préférez le new Foo(){} ou le new Foo(MyType.class); dans votre code.


    Par exemple:

     import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.ArrayDeque; import java.util.Deque; import java.util.NoSuchElementException; /** * Captures and silently ignores stack exceptions upon popping. */ public abstract class SilentStack extends ArrayDeque { public E pop() { try { return super.pop(); } catch( NoSuchElementException nsee ) { return create(); } } public E create() { try { Type sooper = getClass().getGenericSuperclass(); Type t = ((ParameterizedType)sooper).getActualTypeArguments()[ 0 ]; return (E)(Class.forName( t.toSsortingng() ).newInstance()); } catch( Exception e ) { return null; } } } 

    Alors:

     public class Main { // Note the braces... private Deque stack = new SilentStack(){}; public static void main( Ssortingng args[] ) { // Returns a new instance of Ssortingng. Ssortingng s = stack.pop(); System.out.printf( "s = '%s'\n", s ); } } 

    Une approche / solution / solution standard consiste à append un object de class au (x) constructeur (s), comme:

      public class Foo { private Class type; public Foo(Class type) { this.type = type; } public Class getType() { return type; } public T newInstance() { return type.newInstance(); } } 

    Imaginez que vous ayez une superclasse abstraite générique:

     public abstract class Foo {} 

    Et puis vous avez une deuxième classe qui étend Foo avec un Bar générique qui étend T:

     public class Second extends Foo {} 

    Vous pouvez obtenir la classe Bar.class dans la classe Foo en sélectionnant le Type (à partir de la réponse de bert bruynooghe) et en le déduisant de l’instance de Class :

     Type mySuperclass = myFoo.getClass().getGenericSuperclass(); Type tType = ((ParameterizedType)mySuperclass).getActualTypeArguments()[0]; //Parse it as Ssortingng Ssortingng className = tType.toSsortingng().split(" ")[1]; Class clazz = Class.forName(className); 

    Vous devez noter que cette opération n’est pas idéale, c’est donc une bonne idée de mettre en cache la valeur calculée pour éviter de multiples calculs. L’une des utilisations typiques est l’implémentation DAO générique.

    La mise en œuvre finale:

     public abstract class Foo { private Class inferedClass; public Class getGenericClass(){ if(inferedClass == null){ Type mySuperclass = getClass().getGenericSuperclass(); Type tType = ((ParameterizedType)mySuperclass).getActualTypeArguments()[0]; Ssortingng className = tType.toSsortingng().split(" ")[1]; inferedClass = Class.forName(className); } return inferedClass; } } 

    La valeur renvoyée est Bar.class lorsqu’elle est appelée à partir d’une classe Foo dans une autre fonction ou à partir d’une classe Bar.

    Voici une solution de travail:

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

    NOTES: Peut être utilisé uniquement comme superclasse

    1. Doit être étendu avec la classe typée ( Child extends Generic )

    OU

    1. Doit être créé en tant qu’implémentation anonyme ( new Generic() {}; )

    J’ai eu ce problème dans une classe générique abstraite. Dans ce cas particulier, la solution est plus simple:

     abstract class Foo { abstract Class getTClass(); //... } 

    et plus tard sur la classe dérivée:

     class Bar extends Foo { @Override Class getTClass() { return Whatever.class; } } 

    Vous ne pouvez pas le faire en raison d’un type d’effacement. Voir aussi Stack Overflow question Java génériques – type effacement – quand et ce qui se passe .

    Un meilleur itinéraire que la classe que les autres ont suggéré est de transmettre un object capable de faire ce que vous auriez fait avec le cours, par exemple, créer une nouvelle instance.

     interface Factory { T apply(); }  void List make10(Factory factory) { List result = new ArrayList(); for (int a = 0; a < 10; a++) result.add(factory.apply()); return result; } class FooFactory implements Factory> { public Foo apply() { return new Foo(); } } List> foos = make10(new FooFactory()); 

    C’est possible:

     class Foo { Class clazz = (Class) DAOUtil.getTypeArguments(Foo.class, this.getClass()).get(0); } 

    Vous avez besoin de deux fonctions de svn / trunk / dao / src / main / java / com / googlecode / genericdao / dao / DAOUtil.java .

    Pour plus d’explications, voir Réflexion des génériques .

    J’ai une solution (laide mais efficace) pour ce problème, que j’ai utilisé récemment:

     import java.lang.reflect.TypeVariable; public static  Class getGenericClass() { __ ins = new __(); TypeVariable[] cls = ins.getClass().getTypeParameters(); return (Class)cls[0].getClass(); } private final class __ // generic helper class which does only provide type information { private __() { } } 

    J’ai trouvé un moyen générique et simple de le faire. Dans ma classe, j’ai créé une méthode qui renvoie le type générique en fonction de sa position dans la définition de la classe. Supposons une définition de classe comme celle-ci:

     public class MyClass { } 

    Maintenant, créons des atsortingbuts pour conserver les types:

     public class MyClass { private Class aType; private Class bType; private Class cType; // Getters and setters (not necessary if you are going to use them internally) } 

    Vous pouvez ensuite créer une méthode générique qui renvoie le type en fonction de l’index de la définition générique:

      /** * Returns a {@link Type} object to identify generic types * @return type */ private Type getGenericClassType(int index) { // To make it use generics without supplying the class type Type type = getClass().getGenericSuperclass(); while (!(type instanceof ParameterizedType)) { if (type instanceof ParameterizedType) { type = ((Class) ((ParameterizedType) type).getRawType()).getGenericSuperclass(); } else { type = ((Class) type).getGenericSuperclass(); } } return ((ParameterizedType) type).getActualTypeArguments()[index]; } 

    Enfin, dans le constructeur, appelez simplement la méthode et envoyez l’index pour chaque type. Le code complet devrait ressembler à:

     public class MyClass { private Class aType; private Class bType; private Class cType; public MyClass() { this.aType = (Class) getGenericClassType(0); this.bType = (Class) getGenericClassType(1); this.cType = (Class) getGenericClassType(2); } /** * Returns a {@link Type} object to identify generic types * @return type */ private Type getGenericClassType(int index) { Type type = getClass().getGenericSuperclass(); while (!(type instanceof ParameterizedType)) { if (type instanceof ParameterizedType) { type = ((Class) ((ParameterizedType) type).getRawType()).getGenericSuperclass(); } else { type = ((Class) type).getGenericSuperclass(); } } return ((ParameterizedType) type).getActualTypeArguments()[index]; } } 

    Comme expliqué dans d’autres réponses, pour utiliser cette approche ParameterizedType , vous devez étendre la classe, mais cela semble être un travail supplémentaire pour créer une toute nouvelle classe qui l’étend …

    Donc, en rendant la classe abstraite, cela vous oblige à l’étendre, satisfaisant ainsi à l’exigence de sous-classement. (en utilisant @Getter de lombok).

     @Getter public abstract class ConfigurationDefinition { private Class type; ... public ConfigurationDefinition(...) { this.type = (Class) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0]; ... } } 

    Maintenant, pour l’étendre sans définir de nouvelle classe. (Notez le {} à la fin … étendu, mais ne rien écraser – sauf si vous voulez).

     private ConfigurationDefinition myConfigA = new ConfigurationDefinition(...){}; private ConfigurationDefinition myConfigB = new ConfigurationDefinition(...){}; ... Class ssortingngType = myConfigA.getType(); Class fileType = myConfigB.getType(); 
      public  T yourMethodSignature(Class type) { // get some object and check the type match the given type Object result = ... if (type.isAssignableFrom(result.getClass())) { return (T)result; } else { // handle the error } } 

    Si vous étendez ou implémentez une classe ou une interface qui utilise des génériques, vous pouvez obtenir le type générique de la classe / interface parent, sans modifier aucune classe / interface existante.

    Il pourrait y avoir trois possibilités,

    Cas 1 Lorsque votre classe étend une classe utilisant des génériques

     public class TestGenerics { public static void main(Ssortingng[] args) { Type type = TestMySuperGenericType.class.getGenericSuperclass(); Type[] gTypes = ((ParameterizedType)type).getActualTypeArguments(); for(Type gType : gTypes){ System.out.println("Generic type:"+gType.toSsortingng()); } } } class GenericClass { public void print(T obj){}; } class TestMySuperGenericType extends GenericClass { } 

    Cas 2 Lorsque votre classe implémente une interface utilisant des génériques

     public class TestGenerics { public static void main(Ssortingng[] args) { Type[] interfaces = TestMySuperGenericType.class.getGenericInterfaces(); for(Type type : interfaces){ Type[] gTypes = ((ParameterizedType)type).getActualTypeArguments(); for(Type gType : gTypes){ System.out.println("Generic type:"+gType.toSsortingng()); } } } } interface GenericClass { public void print(T obj); } class TestMySuperGenericType implements GenericClass { public void print(Integer obj){} } 

    Cas 3 Lorsque votre interface étend une interface utilisant des génériques

     public class TestGenerics { public static void main(Ssortingng[] args) { Type[] interfaces = TestMySuperGenericType.class.getGenericInterfaces(); for(Type type : interfaces){ Type[] gTypes = ((ParameterizedType)type).getActualTypeArguments(); for(Type gType : gTypes){ System.out.println("Generic type:"+gType.toSsortingng()); } } } } interface GenericClass { public void print(T obj); } interface TestMySuperGenericType extends GenericClass { } 

    En fait, je suppose que vous avez un champ dans votre classe de type T. S’il n’y a pas de champ de type T, quel est l’intérêt d’avoir un type générique? Donc, vous pouvez simplement faire une instance sur ce champ.

    Dans mon cas, j’ai un

      Liste  éléments; 

    dans ma classe, et je vérifie si le type de classe est “Localité” par

      if (items.get (0) instanceof Locality) ...
    

    Bien sûr, cela ne fonctionne que si le nombre total de classes possibles est limité.

    J’utilise une solution de contournement pour cela:

     class MyClass extends Foo { .... } MyClass myClassInstance = MyClass.class.newInstance();