web-dev-qa-db-fra.com

Comment obtenir la classe de type variable en Java Generics

J'ai vu des questions similaires mais elles n'ont pas beaucoup aidé.

Par exemple, j'ai cette classe générique:

public class ContainerTest<T>
{

    public void doSomething()
    {
        //I want here to determinate the Class of the type argument (In this case String)
    }
}

et une autre classe qui utilise cette classe de conteneur

public class TestCase
{

    private ContainerTest<String> containerTest;

    public void someMethod()
    {
        containerTest.doSomething();
    }
}

Est-il possible de déterminer la classe du type argument dans la méthode doSomething () sans ayant un type explicite variable/champ ou n'importe quel constructeur dans la classe ContainerTest?

Mise à jour: format modifié de la classe ContainerTest

18
ZeDonDino

Le seul moyen est de stocker la classe dans une variable d'instance et de l'exiger en tant qu'argument du constructeur:

public class ContainerTest<T>
{
    private Class<T> tClass;
    public ContainerTest(Class<T> tClass) {
        this.tCLass = tClass;
    }

    public void doSomething()
    {
        //access tClass here
    }
}
13
Didier L

Si vous êtes intéressé par la méthode réflexion, j'ai trouvé une solution partielle dans cet article: http://www.artima.com/weblogs/viewpost.jsp?thread=208860

En bref, vous pouvez utiliser les méthodes Java.lang.Class.getGenericSuperclass() et Java.lang.reflect.ParameterizedType.getActualTypeArguments(), mais vous devez sous-classer une super classe parent.

L'extrait suivant fonctionne pour une classe qui directement étend la super-classe AbstractUserType. Voir l'article référencé pour une solution plus générale.

import Java.lang.reflect.ParameterizedType;


public class AbstractUserType<T> {

    public Class<T> returnedClass() {
        ParameterizedType parameterizedType = (ParameterizedType) getClass()
                .getGenericSuperclass();

        @SuppressWarnings("unchecked")
        Class<T> ret = (Class<T>) parameterizedType.getActualTypeArguments()[0];

        return ret;
    }

    public static void main(String[] args) {
        AbstractUserType<String> myVar = new AbstractUserType<String>() {};

        System.err.println(myVar.returnedClass());
    }

}
9
dedek

Il n’existe pas de moyen "propre" d’obtenir l’argument Type générique dans la classe . Au lieu de cela, un modèle courant consiste à transmettre la classe du type générique au constructeur et de la conserver en tant que propriété interne juste comme Implémentation Java.util.EnumMap.

http://docs.Oracle.com/javase/1.5.0/docs/api/Java/util/EnumMap.htmlhttp://grepcode.com/file/repository.grepcode. com/Java/root/jdk/openjdk/6-b14/Java/util/EnumMap.Java

public class ContainerTest<T> {

    Class<T> type;
    T t;

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

    public void setT(T t) {
        this.t = t;
    }

    public T getT() {
        return t;
    }

    public void doSomething() {
        //There you can use "type" property.
    }
}
5
RenaudBlue

Non, ce n'est pas possible à cause de type erasure (les paramètres de type sont compilés en tant que Object + types). Si vous vraiment devez connaître/appliquer le type au moment de l'exécution, vous pouvez stocker une référence à un objet Class

public class ContainerTest<T> {
   private final Class<T> klass;
   private final List<T> list = new ArrayList<T>();

   ContainerTest(Class<T> klass) {
     this.klass = klass;
   }

   Class<T> getElementClass() {
     return klass;
   }

   void add(T t) {
      //klass.cast forces a runtime cast operation
      list.add(klass.cast(t));
   }
}

Utilisation: 

ContainerTest<String> c = new ContainerTest<>(String.class);
2
Javier

Pour apprendre la valeur de T, vous devez la capturer dans une définition de type en sous-classant ContainerTest:

class MyStringClass extends ContainerTest<String> {}

Vous pouvez également le faire avec une classe anonyme si vous voulez:

ContainerTest<String> myStringClass = new ContainerTest<String>{}();

Ensuite, pour résoudre la valeur de T, vous pouvez utiliser TypeTools :

Class<?> stringType = TypeResolver.resolveRawArgument(ContainerTest.class, myStringClass.getClass());
assert stringType == String.class;
1
Jonathan

Si vous pouvez définir comme ceci

public class ContainerTest<T>
{

    public void doSomething(T clazz)
    {

    }
}

Alors c'est possible

0
Arsen Alexanyan

There is est un moyen d’obtenir le type d’exécution du paramètre type en utilisant TypeToken de Guava pour le capturer. L'inconvénient de la solution est que vous devez créer une sous-classe anonyme chaque fois que vous avez besoin d'une instance de Container.

class Container<T> {

    TypeToken<T> tokenOfContainedType = new TypeToken<T>(getClass()) {};

    public Type getContainedType() {
        return tokenOfContainedType.getType();
    }
}

class TestCase {

    // note that containerTest is not a simple instance of Container,
    // an anonymous subclass is created
    private Container<String> containerTest = new Container<String>() {};

    @Test
    public void test() {
        Assert.assertEquals(String.class, containerTest.getContainedType());
    }
}

La clé de cette solution est décrite dans le JavaDoc du constructeur de TypeToken utilisé dans le code ci-dessus:

Les clients créent une sous-classe anonyme anonyme. Cela incorpore le paramètre type dans la hiérarchie des types de la classe anonyme afin que nous puissions le reconstituer au moment de l'exécution malgré l'effacement.

0
zagyi