2009-04-14 2 views
45

Je souhaite créer un tableau de classes, chacune représentant un type disponible dans le système que je suis en train de créer. Toutes les classes concernées sont des sous-classes d'une superclasse commune. Donc, je voudrais faire:Comment utiliser des génériques avec un tableau de classes?

Class<? extends SuperClass>[] availableTypes = { SubClass1.class, SubClass2.class }; 

Cela me donne l'erreur:

Cannot create a generic array of Class<? extends SuperClass>. 

Je reçois le même message si je tente de qualifier la création du tableau sur le côté droit de la initialisation:

Class<? extends SuperClass>[] availableTypes = Class<? extends SuperClass>[] { SubClass1.class, SubClass2.class }; 

je peux obtenir le code pour compiler si je gomment les qualifications génériques:

Class[] availableTypes = { SubClass1.class, SubClass2.class }; 

Mais alors je reçois l'avertissement génériques:

La classe est un type brut. Les références au type générique Class doivent être paramétrées.

J'essaie; J'essaie! :) Aussi, à ce stade, même si cela n'a pas provoqué d'avertissement, je perds une partie de l'interface que j'essayais de définir. Je ne veux pas retourner juste un tableau de classes arbitraires; Je veux retourner un tableau de classes qui sont toutes des sous-classes d'une SuperClasse particulière! Eclipse possède des outils assez puissants pour déterminer les paramètres à utiliser pour corriger les déclarations de génériques, mais dans ce cas, il tombe en panne, comme il a tendance à le faire lorsque vous traitez avec Class. La procédure "Infer Generic Type Arguments" proposée ne modifie en rien le code, laissant l'avertissement.

j'ai pu contourner ce problème en utilisant une collection à la place:

List<Class<? extends SuperClass>> availableTypes = new List<Class<? extends SuperClass>>(); 

Mais quelle est la bonne façon de le faire avec des tableaux?

Répondre

22

Il semble un peu défaitiste, mais des problèmes comme celui-ci sont précisément la raison pour laquelle la plupart des gens évitent de mélanger les tableaux et les génériques. En raison de la façon dont les génériques sont implémentés (type erasure), les tableaux et les génériques ne fonctionneront jamais bien ensemble.

Deux solutions de contournement:

  • bâton à l'aide d'une collection (par exemple ArrayList<Class<? extends SuperClass>>), qui fonctionne aussi bien comme un tableau et permet également l'expansion.
  • Mettez une annotation @SuppressWarnings("unchecked") sur le code créant le tableau avec un commentaire justifiant son utilisation.
2

Le problème est qu'il est illégal de créer un tableau d'un type générique. La seule façon de contourner le problème est de convertir le type générique lorsque vous créez le tableau, mais ce n'est pas une très bonne solution. (Notez qu'il est possible de utiliser un tableau générique, tout simplement pas créer un. Voir this question)

Vous devriez presque toujours à l'aide des listes au lieu de tableaux de toute façon, donc je pense que vous êtes venu avec la meilleure solution déjà .

14

Utilisez cette syntaxe:

Class<? extends SuperClass>[] avail = new Class[] { SubClass1.class, ... }; 

Il vous donnera un avertissement « sans contrôle », et à juste titre, puisque vous pourriez même un objet Class pour un type qui ne couvre pas SuperClass dans le tableau.

11

La meilleure façon de faire cela avec des tableaux est de le faire avec une collection. Pardon! Pour un ensemble complexe de raisons, les tableaux ne fonctionnent pas bien avec les génériques. Les tableaux ont un modèle de covariance différent de celui des objets génériques, ce qui cause finalement les problèmes que vous rencontrez. Par exemple, avec des tableaux, mais pas (normalement) avec des objets génériques, vous pouvez légalement faire:

Object[] myArray = new String[5]; 

pendant que vous ne pouvez pas le faire:

LinkedList<Object> myCollection = new LinkedList<String>(); 

Si vous voulez plus de détails, vous pouvez voir la Arrays In Java Generics la page de l'excellent Generics FAQ

comme Simonn dit, vous pouvez aussi utiliser vos tableaux comme ils sont, et utiliser @SuppressWarnings("unchecked") pour faire taire les avertissements. Cela fonctionnera, mais sans le type de sécurité que les génériques peuvent vous fournir. Si ce sont les performances qui vous inquiètent, utilisez simplement un ArrayList afin d'utiliser une enveloppe fine autour d'un tableau, mais avec toutes les garanties de sécurité fournies par les génériques.

+1

Covariance! Vous avez dit le mauvais mot! –

4

But what's the right way to do this with arrays?

Il n'existe pas de méthode sûre pour ce faire; en utilisant la collection est la bonne approche. Pour voir pourquoi, imaginez si cela a été autorisé. Vous pourriez avoir une situation comme celle-ci:

// Illegal! 
Object[] baskets = new FruitBasket<? extends Citrus>[10]; 

// This is okay. 
baskets[0] = new FruitBasket<Lemon>(); 

// Danger! This should fail, but the type system will let it through. 
baskets[0] = new FruitBasket<Potato>(); 

Le système de type doit détecter si un panier qui est ajoutée au tableau est du type FruitBasket<? extends Citrus> ou un sous-type. Un FruitBasket ne correspond pas et doit être rejeté avec un ArrayStoreException. Mais rien ne se passe!

En raison de l'effacement de type, la machine virtuelle Java ne peut voir que le type d'exécution de la baie. Au moment de l'exécution, nous devons comparer le type de tableau au type d'élément pour nous assurer qu'ils correspondent. Le type de composant d'exécution du tableau est FruitBasket[] après l'effacement du type; De même, le type d'exécution de l'élément est FruitBasket. Aucun problème ne sera détecté - et c'est pourquoi c'est dangereux.

0

Non SÛR type et avec un avertissement de fonte non contrôlée:

Class<? extends SuperClass>[] availableTypes = 
    (Class<? extends SuperClass>[]) 
    (new Class[]{ SubClass1.class, SubClass2.class });