web-dev-qa-db-fra.com

Comment utiliser des génériques avec un tableau de classes?

Je veux créer un tableau de classes, chacune représentant un type qui est disponible dans le système que je construis. Toutes les classes concernées sont les sous-classes d'une superclasse commune. Donc, je voudrais faire:

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 l'initialisation:

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

Je peux obtenir le code à la compilation si je gomment les qualifications génériques:

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

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

La classe est un type brut. Les références à type générique de classe devraient être paramétrés.

J'essaie; J'essaie! :) En outre, à ce stade, même si cela n'a pas provoqué un avertissement, je perds un morceau de l'interface que je tentais de définir. Je ne veux pas revenir juste un tableau de classes arbitraires; Je veux retourner un tableau de classes qui sont toutes les sous-classes d'une superclasse particulière!

Eclipse dispose de quelques outils assez puissants pour déterminer quels paramètres à utiliser pour fixer les déclarations des génériques, mais dans ce cas, il tombe, car il tend à faire lorsque vous traitez avec classe. La procédure " Déduire Arguments type générique ", il offre ne change pas le code du tout, 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?

47
skiphoppy

Il semble un peu défait, mais des problèmes comme celle-ci sont précisément la raison pour laquelle la plupart des gens évitent de mélanger des tableaux et des génériques. En raison de la manière 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:

  • Collez sur l'utilisation d'une collection (E.G. ArrayList<Class<? extends SuperClass>>), Qui fonctionne aussi bien qu'un tableau et permet également une expansion.
  • Placez une @SuppressWarnings("unchecked") Annotation sur le code Création de la matrice avec un commentaire en justifiant son utilisation.
23
Simon Nickerson

Utilisez cette syntaxe:

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

Cela vous donnera un avertissement "non coché", et à juste titre, puisque vous y compris un objet Class pour un type qui ne prolonge pas SuperClass dans le tableau.

14
erickson

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

Object[] myArray = new String[5];

pendant que vous ne pouvez pas faire cela:

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

Si vous voulez plus de détails, vous pouvez voir les matrices in Java Generics page de l'excellent génériques FAQ

Comme le dit Simonn, vous pouvez également utiliser vos matrices comme ils sont, et utilisez @SuppressWarnings("unchecked") pour faire taire les avertissements. Cela fonctionnera, mais sans la sécurité de type que les génériques peuvent vous fournir. Si c'est une performance que vous vous inquiétez, utilisez simplement un ArrayList vous suffit donc d'utiliser une fine enveloppe autour d'un tableau, mais avec toutes les garanties de sécurité fournies par des génériques.

12
Eddie

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

Il n'y a pas de moyen de type de type pour le faire; tiliser la collection est la bonne approche. Pour voir pourquoi, imaginez si cela était 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 ajouté à la matrice est du type FruitBasket<? extends Citrus> ou un sous-type. Un masque fruitier ne correspond pas et doit être rejeté avec un ArrayStoreException. Mais rien ne se passe!

En raison de l'effacement de type, la JVM ne peut voir que le type d'exécution de la matrice. Au moment de l'exécution, nous devons comparer le type de tableau au type d'élément pour vous assurer qu'ils correspondent. Le type de composant d'exécution du tableau est FruitBasket[] après effacement de 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.

5
John Feminella

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 se déplacer est de lancer au 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 une matrice générique, tout simplement pas en créer un: voir cette question .)

Vous devriez presque toujours utiliser des listes au lieu de tableaux de toute façon, alors je pense que vous avez déjà proposé la meilleure solution.

2
Michael Myers

Ne pas taper sûr et avec un avertissement non coché:

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