60 votes

Génériques Java dans ArrayList.toArray ()

Disons que vous avez une liste de tableaux définis comme suit:

ArrayList<String> someData = new ArrayList<>();

Plus tard dans votre code, en raison de génériques, vous pouvez dire ceci:

String someLine = someData.get(0);

Et le compilateur sait d'emblée qu'il sera d'obtenir une chaîne de caractères. Yay génériques! Toutefois, cela ne fonctionne pas:

String[] arrayOfData = someData.toArray();

toArray() toujours retourner un tableau d'Objets, pas de générique qui a été défini. Pourquoi ne l' get(x) méthode de savoir de quoi il retourne, mais toArray() valeurs par défaut pour les Objets?

60voto

Sudhir Singh Points 3392

Si vous regardez à la mise en œuvre de l' toArray(T[] a) de ArrayList<E> de la classe, c'est comme:

public <T> T[] toArray(T[] a) {
    if (a.length < size)
        // Make a new array of a's runtime type, but my contents:
        return (T[]) Arrays.copyOf(elementData, size, a.getClass());
    System.arraycopy(elementData, 0, a, 0, size);
    if (a.length > size)
        a[size] = null;
    return a;
}

Le problème avec cette méthode est que vous avez besoin pour passer un tableau du même type générique. Considérons maintenant si cette méthode ne prendre aucun argument alors la mise en œuvre serait quelque chose de similaire:

public <T> T[] toArray() {
    T[] t = new T[size]; // compilation error
    return Arrays.copyOf(elementData, size, t.getClass());
}

Mais le problème, c'est que vous ne pouvez pas créer générique tableaux en Java , car le compilateur ne sait pas exactement ce qu' T représente. En d'autres termes la création de la matrice d'une non-reifiable type (JLS §4.7) n'est pas autorisé en Java.

Un autre élément important de devis à partir de la Matrice de Magasin Exception (JLS §10.5):

Si le type de composant d'un tableau n'ont pas été reifiable (§4.7), la Machine Virtuelle Java n'a pas pu effectuer le store check décrit dans la paragraphe précédent. C'est pourquoi la création de la matrice d'expression avec un non reifiable type d'élément est interdit (§15.10.1).

C'est pourquoi Java a fourni une version surchargée toArray(T[] a).

Je vais remplacer la méthode toArray() pour lui dire qu'il sera de retour une tableau d'E.

Ainsi, au lieu d'annuler toArray(), vous devez utiliser toArray(T[] a).

Ne peut pas Créer des Instances de Paramètres de Type de Java Doc peut également être intéressant pour vous.

21voto

AdamSkywalker Points 4028

L'information générale est effacé au moment de l'exécution. JVM ne savez pas si votre liste est - List<String> ou List<Integer> (au moment de l'exécution T en List<T> est résolu que Object), de sorte que le seul type de tableau est Object[].

Vous pouvez utiliser toArray(T[] array) - dans ce cas JVM pouvez utiliser la classe d'un tableau donné, vous pouvez le voir dans l' ArrayList mise en œuvre:

public <T> T[] toArray(T[] a) {
    if (a.length < size)
        // Make a new array of a's runtime type, but my contents:
        return (T[]) Arrays.copyOf(elementData, size, a.getClass());

16voto

ach Points 2279

Si vous regardez le Javadoc pour l'interface List , vous remarquerez une seconde forme de toArray : <T> T[] toArray(T[] a) .

En fait, la Javadoc donne même un exemple de la façon dont vous faites exactement ce que vous voulez faire:

String[] y = x.toArray(new String[0]);

5voto

senseiwu Points 1716

Je peux, et d'utiliser un itérateur au lieu de faire un tableau parfois, mais cela m'a toujours semblé étrange pour moi. Pourquoi le get(x) méthode de savoir de quoi il retourne, mais toArray() la valeur par défaut pour les Objets? Ses comme à mi-chemin dans la concevoir, ils ont décidé que ce n'était pas nécessaire ici??

Que l'intention de la question ne semble pas se déplacer en toArray() avec les génériques, plutôt sur la compréhension de la conception de méthodes dans l' ArrayList classe, je voudrais ajouter:

ArrayList est une classe générique, comme il est déclaré comme

public class ArrayList<E> extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable

ce qui rend possible l'utilisation de méthodes Génériques telles que public E get(int index) au sein de la classe.

Mais si une méthode telle que l' toArray() n'est pas de revenir Eplutôt E[] puis les choses commencent à obtenir un peu délicat. Il ne serait pas possible d'offrir une signature comme public <E> E[] toArray() car il n'est pas possible de créer des génériques de tableaux.

Création de tableaux de se produire lors de l'exécution, et en raison du Type d'effacement, d'exécution Java dispose d'aucune information précise du type représenté par E. La seule solution maintenant est de passer le type en tant que paramètre à la méthode, et donc la signature public <T> T[] toArray(T[] a) où les clients sont obligés de passer au type requis.

Mais d'un autre côté, il travaille pour public E get(int index) parce que si vous regardez à la mise en œuvre de la méthode, vous constaterez que même si la méthode permet l'utilisation de la même matrice de l'Objet pour le retour de l'élément à l'index spécifié, il est coulé à l' E

E elementData(int index) {
    return (E) elementData[index];
}

C'est le compilateur Java qui, au moment de la compilation remplace E avec Object

5voto

newacct Points 42530

Pertinentes chose à noter est que les tableaux en Java connaître leur type de composant à l'exécution. String[] et Integer[] sont des classes différentes au moment de l'exécution, et vous pouvez demander des tableaux pour leur type de composant à l'exécution. Par conséquent, un type de composant est nécessaire à l'exécution (soit à coder en dur une reifiable type de composant au moment de la compilation avec new String[...], ou à l'aide de Array.newInstance() et le passage d'un objet de classe) pour créer un tableau.

D'autre part, les arguments de type dans les génériques n'existent pas au moment de l'exécution. Il n'y a absolument aucune différence d'exécution entre un ArrayList<String> et ArrayList<Integer>. Il est tout simplement ArrayList.

C'est la raison fondamentale pour laquelle vous ne pouvez pas simplement prendre un List<String> et d'obtenir un String[] sans passer dans le type de composant séparément en quelque sorte -- vous auriez à obtenir le type de composant informations de quelque chose qui n'a pas de type de composant de l'information. Clairement, c'est impossible.

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