web-dev-qa-db-fra.com

Java Classe générique - Déterminer le type

Si je crée une classe Java pour être générique, telle que:

public class Foo<T>

Comment peut-on déterminer en interne à cette classe, quel "T" a fini par être?

public ???? Bar()
{
    //if its type 1
    //    do this
    //if its type 2
    //    do this
    //if its type 3
    //    do this
    //if its type 4
    //    do this
}

J'ai fouillé le Java et j'ai joué avec les trucs Reflection, instanceof, getClass, .class, etc., mais je n'arrive pas à en faire des têtes ou des queues. J'ai l'impression que Je suis proche et j'ai juste besoin de combiner un certain nombre d'appels, mais continuez à manquer.

Pour être plus précis, j'essaie de déterminer si la classe a été instanciée avec l'un des 3 types possibles.

49
Ben Lakey

J'ai utilisé une solution similaire à ce qu'il explique ici pour quelques projets et je l'ai trouvée assez utile.

http://blog.xebia.com/2009/02/07/acessing-generic-types-at-runtime-in-Java/

Pour l'essentiel, il utilise les éléments suivants:

 public Class returnedClass() {
     ParameterizedType parameterizedType = (ParameterizedType)getClass()
                                                 .getGenericSuperclass();
     return (Class) parameterizedType.getActualTypeArguments()[0];
}
58
jnt30

Contrairement à .NET Java les génériques sont implémentés par une technique appelée "effacement de type".

Cela signifie que le compilateur utilisera les informations de type lors de la génération des fichiers de classe, mais ne transférera pas ces informations au code d'octet. Si vous regardez les classes compilées avec javap ou des outils similaires, vous constaterez qu'un List<String> est un simple List (de Object) dans le fichier de classe, tout comme il l'était dans le code pré-Java-5.

Le code accédant à la liste générique sera "réécrit" par le compilateur pour inclure les conversions que vous auriez à écrire vous-même dans les versions antérieures. En effet, les deux fragments de code suivants sont identiques du point de vue du code octet une fois que le compilateur en a terminé:

Java 5:

List<String> stringList = new ArrayList<String>();
stringList.add("Hello World");
String hw = stringList.get(0);

Java 1.4 et versions antérieures:

List stringList = new ArrayList();
stringList.add("Hello World");
String hw = (String)stringList.get(0);

Lors de la lecture des valeurs d'une classe générique dans Java 5, le transtypage nécessaire en paramètre de type déclaré est automatiquement inséré. Lors de l'insertion, le compilateur vérifie la valeur que vous essayez de saisir et abandonne avec une erreur s'il ne s'agit pas d'une chaîne.

Le tout a été fait pour garder les anciennes bibliothèques et le nouveau code généré interopérables sans avoir besoin de recompiler les bibliothèques existantes. C'est un avantage majeur par rapport à la manière .NET où les classes génériques et non génériques vivent côte à côte mais ne peuvent pas être échangées librement.

Les deux approches ont leurs avantages et leurs inconvénients, mais c'est comme ça en Java.

Pour revenir à votre question d'origine: Vous ne pourrez pas accéder aux informations de type lors de l'exécution, car elles ne sont tout simplement plus là, une fois que le compilateur a fait son travail. Cela est sûrement limitant à certains égards et il existe des moyens grincheux qui sont généralement basés sur le stockage d'une instance de classe quelque part, mais ce n'est pas une fonctionnalité standard.

44
Daniel Schneller

En raison de type erasure , il n'y a aucun moyen de le faire directement. Ce que vous pourriez faire, cependant, est de passer un Class<T> dans le constructeur et maintenez-le dans votre classe. Ensuite, vous pouvez le comparer aux trois types Class possibles que vous autorisez.

Cependant, s'il n'y a que trois types possibles, vous voudrez peut-être envisager de refactoriser en enum à la place.

12
Michael Myers

Le problème est que la plupart des éléments génériques disparaîtront pendant la compilation.

Une solution courante consiste à enregistrer le type lors de la création de l'objet.

Pour une brève introduction au comportement d'effacement de type de Java lisez ceci page

3
Janusz

Si vous connaissez quelques types spécifiques significatifs, vous devez créer des sous-classes de votre type générique avec l'implémentation.

Alors

public class Foo<T>

public ???? Bar()
{
    //else condition goes here
}

Et alors

public class DateFoo extends Foo<Date>

public ???? Bar()
{
    //Whatever you would have put in if(T == Date) would go here.
}
1
Ryan Shillington

L'intérêt d'une classe générique est que vous n'avez pas besoin de connaître le type utilisé ...

0
PaulJWilliams

Il semble que ce que vous voulez n'est en fait pas une classe générique, mais une interface avec un certain nombre d'implémentations différentes. Mais peut-être que cela deviendrait plus clair si vous déclariez votre objectif réel et concret.

0
Michael Borgwardt

Je suis d'accord avec Visage. Les génériques sont destinés à la validation au moment de la compilation, et non au typage dynamique à l'exécution. On dirait que ce dont vous avez besoin est vraiment juste le modèle d'usine. Mais si votre "faire ceci" n'est pas une instanciation, alors une énumération simple fonctionnera probablement aussi bien. Comme ce que Michael a dit, si vous avez un exemple un peu plus concret, vous obtiendrez de meilleures réponses.

0
aberrant80