[JAVA] Punkt 28: Listen Arrays vorziehen
28. Verwenden Sie Listen anstelle von Arrays
- Sequenzen unterscheiden sich in zwei wichtigen Punkten im Vergleich zu generischen Typen.
- Die Sequenz ist kovariant. Beispielsweise ist die Unterklasse eine Unterklasse der Superklasse, und die Unterklasse [] ist eine Unterklasse der Superklasse [].
Auf der anderen Seite sind Generika nicht auf der gleichen Seite. Liste und Liste dürfen in keiner der beiden Klassen Typ1 und Typ2 eine Eltern-Kind-Beziehung haben.
Aufgrund dieser Eigenschaft ist im folgenden Code das Ergebnis erst bekannt, wenn das Array ausgeführt wird, aber die Liste ist zur Kompilierungszeit bekannt.
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
}
}
-
Die Reihenfolge wird geändert. Das Array kennt das Element zur Laufzeit und verarbeitet das Element.
Generika hingegen werden durch Löschen implementiert. Generika sind nur zur Kompilierungszeit typbeschränkt, und Elementtypinformationen werden zur Laufzeit verworfen.
-
Aufgrund der oben genannten grundlegenden Unterschiede können Sequenzen und Generika nicht gut gemischt werden. Beispielsweise wird Code wie "Neue Liste []" zur Kompilierungszeit abgelehnt.
Ich werde den Grund erklären, warum das generische Array nicht erstellt werden kann, vorausgesetzt, dass die erste Zeile des folgenden Codes keinen Kompilierungsfehler verursacht (tatsächlich ist die erste Zeile ein Kompilierungsfehler).
In der zweiten Zeile wird eine Liste mit einem Element erstellt.
In der dritten Zeile wird die Liste im Object-Array gespeichert. Dies ist wahr, weil die Sequenz kovariant ist.
In der 4. Zeile wird List im Object-Array gespeichert. Dies löst keine ArrayStoreException aus, da List nur eine List und List [] nur eine List [] ist.
Dann ist das aus List in der 5. Zeile extrahierte Element Integer, und ClassCastException tritt auf.
Um dies zu verhindern, weisen Arrays vom generischen Typ einen Kompilierungsfehler auf.
// 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)
Typen wie * E und List werden als ** nicht überprüfbar ** bezeichnet. Die intuitive Bedeutung dieses Wortes ist, dass es zur Laufzeit weniger Informationen enthält als zur Kompilierungszeit. Nur generische Typen, die den Platzhalter verwenden, können überprüft werden, aber eine Reihe von Generika, die Platzhalter verwenden, hat keinen praktischen Wert.
- Da kein generisches Typarray erstellt werden kann, ist die Verwendung einer Methode mit Argumenten variabler Länge schwierig. Bei Verwendung einer Argumentmethode mit variabler Länge wird ein Array erstellt, das das Argument mit variabler Länge enthält. Wenn diese Elementtypen nicht überprüfbar sind, wird eine Warnung ausgegeben. Verwenden Sie dazu die Annotation `` `SafeVarargs``` (Item32).
- Wenn Sie mit einer Situation konfrontiert sind, in der Sie kein generisches Typarray erstellen oder in ein Array umwandeln können, verwenden Sie den Auflistungstyp ** List > ** anstelle des Elementtyparrays ** E [] **. wir müssen. Dies gewährleistet Typensicherheit und Interoperabilität auf Kosten der Einfachheit und Leistung.
- Angenommen, Sie schreiben eine Chooser-Klasse, die eine Sammlung in ihrem Konstruktor verwendet und die Elemente in dieser Sammlung mit der einzigen Methode zufällig abruft.
Wenn Sie es ohne Verwendung von Generika schreiben, ist es wie folgt. Wenn Sie diese Klasse verwenden, müssen Sie bei Verwendung der Methode immer den gewünschten Typ umwandeln. Wenn die Umwandlung fehlschlägt, wird zur Laufzeit ein Fehler angezeigt.
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)];
}
}
Laut Item29 habe ich Folgendes in Generics geschrieben. Es ist etwas redundant und führt zu Leistungseinbußen, löst jedoch zur Laufzeit keine ClassCastException aus.
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()));
}
}