44 votes

Tableaux génériques en Java

OK, je suis en train de google sur le Web, et je n'arrive pas à trouver de solution à mon problème. J'ai trouvé beaucoup de solutions, mais pas toutes celles qui conviennent.

J'ai besoin de créer un tableau de génériques. Mais le type générique lui-même s'étend à Comparable. Quand j'essaye ce qui suit:

 public class Hash<T extends Comparable<String>> {
    private T[] hashTable;
    private int tableSize;

    Hash(int records, double load) {
        tableSize = (int)(records / loadFactor);
        tableSize = findNextPrime(tableSize);
        hashTable = (T[])(new Object[tableSize]);  //Error: Ljava.lang.Object; cannot be cast to [Ljava.lang.Comparable;
    }
}
 

Le problème est que l’objet ne peut pas être converti en un générique qui étend Comparable. Y a-t-il un moyen de contourner ceci?

78voto

cletus Points 276888

Les génériques et les tableaux ne se mélangent pas, en fait. La réponse courte est que vous pouvez contourner ce problème. La plus longue réponse, c'est que vous ne devriez probablement pas et je vais vous expliquer pourquoi.

Vous pouvez utiliser Array.newInstance() comme ceci:

private Comparable[] hashtable;

...

hashtable = (Comparable)Array.newInstance(Comparable.class, tableSize);

mais vous ne pouvez pas créer un tableau de votre type paramétré.

Les tableaux sont covariants. Cela signifie qu'ils conservent le type de leurs éléments au moment de l'exécution. Java les génériques ne sont pas. Ils utilisent le type d'effacement en gros de masque de la conversion implicite qui se passe. Il est important de comprendre que.

Ainsi, lorsque vous créez un tableau d'Objets que vous ne pouvez pas jeter à, disons, un Comparable tableau (ou autre) parce que ce n'est pas correct.

Pour vous donner un exemple. Avec les génériques c'est parfaitement légal:

List<String> list = new ArrayList<String>();
List<Integer> list2 = (List<Integer>)list;
list.add(3);

C'est aussi pourquoi vous ne pouvez pas faire ceci:

public <T> T newInstance(T t) {
  return new T(); // error!
}

c'est à dire à exécution il n'y a aucune connaissance de la T de la classe. C'est pourquoi le code ci-dessus est le plus souvent écrit que:

public <T> T newInstance(T t, Class<T> clazz) {
  return clazz.newInstance();
}

parce que n'est pas leur runtime type de l'argument générique. Mais avec les tableaux:

String arr[] = new String[10];
Integer arr2[] = (Integer[])arr; // error!

Ce que vous devez faire dans ce cas (à mon humble avis) n'est pas à l'aide de tableaux, mais à l'aide d'un ArrayList. En toute honnêteté, il ya très peu de raisons d'utiliser des tableaux sur un ArrayList et les génériques est qu'un exemple de cela.

Pour une meilleure et plus complète voir l'explication de l' (excellent) Java Génériques FAQ:

Puis-je créer un tableau dont le type de composant est un béton de type paramétré?

Non, car il n'est pas de type sécurisé.

Les tableaux sont covariants, ce qui signifie que un tableau de supertype des références est un supertype d'un tableau de sous-type les références. C'est, Object[] est un supertype de String[] et une chaîne tableau peut être consulté par le biais d'un référence variable de type Object[].

...

16voto

matt Points 33799

Les autres réponses ici préconisent généralement une meilleure approche à cet égard (en particulier la recommandation d'utiliser un ArrayList à la place), mais une réponse simple dans ce cas spécifique pourrait être la suivante:

 hashTable = (T[])(new Comparable[tableSize]);
 

(ie créer un tableau de type raw Comparable au lieu de Object)

Si vous encapsulez correctement tous les accès à ce tableau dans votre objet Hash, cela devrait fonctionner, mais (comme l'expliquent les autres réponses), vous pourriez rester vulnérable.

4voto

Dónal Points 61837

Le cast vous êtes tentant

(T[])(new Object[tableSize]);

échoue, parce que les éléments du tableau sont les instances de l'Objet. L'objet n'a pas d'étendre Comparable<String>, de sorte que le cast (T[]) échoue parce que T est défini comme suit:

T extends Comparable<String>

Pour résoudre ce problème:

  • Instancier le tableau de sorte que c'est des éléments sont des instances d'une classe qui ne s'étendent Comparable<String>
  • Variation hashTable à partir d'un Tableau (qui n'est pas un type générique), pour une collection générique type, par exemple, List<T> hashTable = new ArrayList<T>(tableSize>)

1voto

Chris Dail Points 11406

Vous rencontrez souvent des problèmes lorsque vous devez instancier un type générique. Le moyen le plus simple de contourner ce problème est de transmettre la classe de qui sera stockée dans le constructeur. De cette façon, vous pouvez construire à partir du type actuel. Essayez quelque chose comme ça:

 public class Hash<T extends Comparable<String>>
{
  Hash(int records, double load, Class<T> class)
  {
    tableSize = (int)(records / loadFactor);
    tableSize = findNextPrime(tableSize);

    hashTable = java.lang.reflect.Array.newInstance(class, tableSize);
  }

private T[] hashTable;
private int tableSize;

}
 

0voto

vnportnoy Points 111

La distribution forcée suggérée par d’autres personnes n’a pas fonctionné pour moi, ce qui constitue une exception.

Cependant, cette distribution implicite a bien fonctionné:

 Item<K>[] array = new Item[SIZE];
 

où Item est une classe que j'ai définie et qui contient le membre:

 private K value;
 

De cette façon, vous obtenez un tableau de type K (si l'élément n'a que la valeur) ou tout type générique que vous souhaitez définir dans la classe Item.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X