Liste Java T toArray (T a) implémentation

Je regardais juste la méthode définie dans l’interface List: T[] toArray(T[] a) , et j’ai une question. Pourquoi est-ce générique? À cause de cela, la méthode n’est pas complète. Le fragment de code suivant comstack mais provoque une ArrayStoreException :

 List list = new ArrayList(); list.add(1); list.add(2); Ssortingng[] ssortingngArray = list.toArray(new Ssortingng[]{}); 

Il me semble que si toArray n’était pas générique et prenait le paramètre de type List, ce serait mieux.

J’ai écrit exemple de jouet et c’est ok sans générique:

 package test; import java.util.Arrays; public class TestGenerics { private Object[] elementData = new Object[10]; private int size = 0; public void add(E e) { elementData[size++] = e; } @SuppressWarnings("unchecked") //I took this code from ArrayList but it is not generic public E[] toArray(E[] a) { if (a.length  size) a[size] = null; return a; } public static void main(Ssortingng[] args) { TestGenerics list = new TestGenerics(); list.add(1); list.add(2); list.add(3); //You don't have to do any casting Integer[] n = new Integer[10]; n = list.toArray(n); } } 

Y a-t-il une raison pour laquelle cela est déclaré?

Des javadocs :

À l’instar de la méthode toArray (), cette méthode sert de pont entre les API basées sur les baies et les API de collection. En outre, cette méthode permet un contrôle précis du type d’exécution du tableau en sortie et peut, dans certaines circonstances, être utilisée pour réduire les coûts d’allocation.

Cela signifie que le programmeur contrôle quel type de tableau il devrait être.

Par exemple, pour votre ArrayList au lieu d’un tableau Integer[] , vous pouvez vouloir un tableau Number[] ou Object[] .

En outre, la méthode vérifie également le tableau transmis. Si vous transmettez un tableau disposant de suffisamment d’espace pour tous les éléments, la méthode toArray réutilise ce tableau. Ça signifie:

 Integer[] myArray = new Integer[myList.size()]; myList.toArray(myArray); 

ou

 Integer[] myArray = myList.toArray(new Integer[myList.size()]); 

a le même effet que

 Integer[] myArray = myList.toArray(new Integer[0]); 

Notez que dans les versions antérieures de Java, cette dernière opération utilisait reflection pour vérifier le type de tableau, puis construire dynamicment un tableau du type approprié. En passant en premier lieu à un tableau correctement dimensionné, il n’était pas nécessaire d’utiliser la reflection pour allouer un nouveau tableau dans la méthode toArray . Ce n’est plus le cas et les deux versions peuvent être utilisées de manière interchangeable.

Il est déclaré génériquement pour que vous puissiez écrire du code tel que

 Integer[] intArray = list.toArray(new Integer[0]); 

sans jeter le tableau qui revient.

Il est déclaré avec l’annotation suivante:

 @SuppressWarnings("unchecked") 

En d’autres termes, Java vous fait confiance pour passer un paramètre de tableau du même type afin que votre erreur ne se produise pas.

La raison pour laquelle la méthode a cette signature est que l’API toArray est antérieure aux génériques: la méthode

  public Object[] toArray(Object[] a) 

a été introduit dès Java 1.2.

Le générique correspondant qui remplace Object par T a été introduit comme option rétro-compatible à 100%:

 public  T[] toArray(T[] a) 

Le passage de la signature à générique permet aux appelants d’éviter la conversion: avant Java 5, les appelants devaient procéder comme suit:

 Ssortingng[] arr = (Ssortingng[])ssortingngList.toArray(new Ssortingng[ssortingngList.size()]); 

Maintenant, ils peuvent faire le même appel sans casting:

 Ssortingng[] arr = ssortingngList.toArray(new Ssortingng[ssortingngList.size()]); 

MODIFIER :

Une signature plus “moderne” pour la méthode toArray serait une paire de surcharges:

 public  T[] toArray(Class elementType) public  T[] toArray(Class elementType, int count) 

Cela fournirait une alternative plus expressive et tout aussi polyvalente à la signature de la méthode actuelle. Il existe également une implémentation efficace de cette Array.newInstance(Class,int) avec la Array.newInstance(Class,int) en place. Changer la signature de cette manière ne serait cependant pas compatible avec les versions antérieures.

Il est ClassCastException type – il ne provoque pas d’ ClassCastException . C’est généralement ce que signifie “type-safe”.

ArrayStoreException est différent. Si vous incluez ArrayStoreException dans “not-safe”, tous les tableaux en Java ne le sont pas.

Le code que vous avez publié produit également une ArrayStoreException . Essayez simplement:

 TestGenerics list = new TestGenerics(); list.add(1); Ssortingng[] n = new Ssortingng[10]; list.toArray(n); // ArrayStoreException 

En fait, il n’est tout simplement pas possible d’autoriser l’utilisateur à transmettre un tableau du type qu’il souhaite obtenir et en même temps de ne pas avoir ArrayStoreException . Parce que toute signature de méthode qui accepte un tableau d’un certain type autorise également les tableaux de sous-types.

Donc, puisqu’il n’est pas possible d’éviter ArrayStoreException , pourquoi ne pas le rendre aussi générique que possible? Pour que l’utilisateur puisse utiliser un tableau d’un type non lié s’il sait que tous les éléments seront des instances de ce type?

Je pense que dasblinkenlight a probablement raison de dire que cela a quelque chose à voir avec la génération d’une méthode existante, et la compatibilité totale est une chose subtile à réaliser.

Le sharepoint beny23 est également très bon – la méthode devrait accepter les supertypes de E[] . On pourrait tenter

   T[] toArray(T[] a) 

mais Java n’autorise pas super sur une variable de type, faute de cas d’utilisation 🙂

(edit: nope, ce n’est pas un bon cas d’utilisation pour super , voir https://stackoverflow.com/a/2800425/2158288 )

La raison pour laquelle cette méthode est telle qu’elle est est principalement historique.

Il existe une différence entre les classes génériques et les types de tableaux: alors que les parameters de type de la classe générique sont effacés à l’exécution, le type des éléments de tableaux ne l’est pas. Ainsi, au moment de l’exécution, la machine virtuelle Java ne voit aucune différence entre List et List , mais une différence entre Integer[] et Ssortingng[] ! La raison de cette différence est que les tableaux ont toujours été présents, à partir de Java 1.0, alors que les génériques n’ont été que ajoutés (de manière rétrocompatible) dans Java 1.5.

L’API Collections a été ajoutée à Java 1.2, avant l’introduction des génériques. A cette époque, l’interface List contenait déjà une méthode

 Object[] toArray(Object[] a); 

(voir cette copie du 1.2 JavaDoc ). C’était le seul moyen de créer un tableau avec un type d’exécution spécifié par l’utilisateur: le paramètre a servi de jeton de type, c’est-à-dire qu’il déterminait le type d’exécution du tableau renvoyé (notez que si A est une sous-classe de B , A[] est considéré comme un sous-type de B[] bien que la List ne soit pas un sous-type de la List ).

Lorsque les génériques ont été introduits dans Java 1.5, de nombreuses méthodes existantes ont été génériques et la méthode toArray est devenue

  T[] toArray(T[] a); 

qui, après effacement de type, a la même signature que la méthode non générique originale.