[JAVA] Élément 28: Préférer les listes aux tableaux
28. Utilisez des listes plutôt que des tableaux
- Les séquences diffèrent de deux manières importantes par rapport aux types génériques.
- La séquence est covariante. Par exemple, la classe Sub est une sous-classe de la classe Super et la classe Sub [] est une sous-classe de la classe Super [].
En revanche, les génériques ne sont pas du même côté. List et List ne peuvent pas avoir de relation parent-enfant dans deux classes Type1 et Type2.
En raison de cette propriété, dans le code suivant, le résultat n'est pas connu tant que le tableau n'est pas exécuté, mais la liste est connue au moment de la compilation.
package tryAny.effectiveJava;
public class GenericsTest5 {
public static void main(String[] args) {
Object[] objAry = new Long[1];
objAry[0] = "aa";
// Won't compile!
// List<Object> ol = new ArrayList<Long>(); // Incompatible
}
}
-
La séquence est réifiée. Le tableau connaît l'élément au moment de l'exécution et traite l'élément.
Les génériques, en revanche, sont mis en œuvre par effacement. Les génériques sont limités au type uniquement au moment de la compilation et les informations de type d'élément sont ignorées au moment de l'exécution.
-
En raison des différences fondamentales ci-dessus, les séquences et les génériques ne peuvent pas être bien mélangés. Par exemple, un code tel que
new List [] '' `` est rejeté au moment de la compilation.
J'expliquerai la raison pour laquelle le tableau générique ne peut pas être créé, en supposant que la première ligne du code suivant ne provoque pas d'erreur de compilation (en fait, la première ligne est une erreur de compilation).
La deuxième ligne crée un List avec un élément.
La troisième ligne stocke la liste dans le tableau Object. Cela est vrai car la séquence est covariante.
La 4ème ligne stocke List dans le tableau Object. Cela ne déclenche pas une ArrayStoreException car List est juste une List et List [] est juste une List [].
Ensuite, l'élément extrait de List sur la 5ème ligne est Integer et ClassCastException se produit.
Pour éviter cela, les tableaux de types génériques ont une erreur de compilation.
// Why generic array creation is illegal - won't compile!
List<String>[] stringLists = new List<String>[1]; // (1)
List<Integer> intList = List.of(42); // (2)
Object[] objects = stringLists; // (3)
objects[0] = intList; // (4)
String s = stringLists[0].get(0); // (5)
Les types tels que * E et List sont appelés ** non réifiable **. La signification intuitive de ce mot est qu'il contient moins d'informations à l'exécution qu'au moment de la compilation. Seuls les types génériques utilisant le caractère générique? Sont réifiables, mais un tableau de génériques utilisant des caractères génériques n'a aucune valeur pratique.
- Comme un tableau de types génériques ne peut pas être créé, il devient difficile à gérer lors de l'utilisation d'une méthode avec des arguments de longueur variable. Lors de l'utilisation d'une méthode d'argument de longueur variable, un tableau est créé pour contenir l'argument de longueur variable. Si ces types d'éléments ne sont pas réifiables, un avertissement sera émis. Pour gérer cela, utilisez l'annotation `` SafeVarargs '' (Item32).
- Si vous êtes confronté à une situation où vous ne pouvez pas créer un tableau de type générique ou transtyper en tableau, utilisez le type de collection ** List > ** plutôt que le tableau de type d'élément ** E [] **. nous devons. Cela garantit la sécurité et l'interopérabilité des types au détriment de la simplicité et des performances.
- Par exemple, supposons que vous écrivez une classe Chooser qui prend une collection dans son constructeur et récupère de manière aléatoire les éléments de cette collection avec la seule méthode.
Si vous l'écrivez sans utiliser de génériques, ce sera comme suit. Si vous utilisez cette classe, vous devez toujours convertir le type souhaité lorsque vous utilisez la méthode, et si la conversion échoue, vous obtiendrez une erreur lors de l'exécution.
package tryAny.effectiveJava;
import java.util.Collection;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
public class Chooser1 {
private final Object[] choiceArray;
public Chooser1(Collection choices) {
choiceArray = choices.toArray();
}
public Object choose() {
Random rnd = ThreadLocalRandom.current();
return choiceArray[rnd.nextInt(choiceArray.length)];
}
}
Selon Item29, j'ai écrit ce qui suit dans Generics. C'est un peu redondant et une dégradation des performances, mais cela ne lève pas d'exception ClassCastException au moment de l'exécution.
package tryAny.effectiveJava;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
//List-based Chooser - typesafe
public class Chooser2<T> {
private final List<T> choiceList;
public Chooser2(Collection<T> choices) {
choiceList = new ArrayList<>(choices);
}
public T choose() {
Random rnd = ThreadLocalRandom.current();
return choiceList.get(rnd.nextInt(choiceList.size()));
}
}