JavaRush /Java-Blog /Random-DE /Verwendung von Varargs bei der Arbeit mit Generika

Verwendung von Varargs bei der Arbeit mit Generika

Veröffentlicht in der Gruppe Random-DE
Hallo! In der heutigen Lektion werden wir uns weiterhin mit Generika befassen. Es ist einfach so, dass das ein großes Thema ist, aber man kann nirgendwo hingehen – das ist ein äußerst wichtiger Teil der Sprache :) Wenn Sie die Oracle-Dokumentation zu Generika studieren oder Leitfäden im Internet lesen, werden Sie auf die Begriffe stoßen Nicht reifizierbare Typen und reifizierbare Typen . Was für ein Wort ist „Reifiable“? Selbst wenn mit Englisch alles gut ist, ist es unwahrscheinlich, dass Sie es kennengelernt haben. Versuchen wir zu übersetzen! Verwendung von Varargs bei der Arbeit mit Generika – 2
*Danke Google, du hast sehr geholfen -_-*
Ein reifizierbarer Typ ist ein Typ, dessen Informationen zur Laufzeit vollständig verfügbar sind. In der Java-Sprache umfassen diese Primitive, Rohtypen und nicht generische Typen. Im Gegensatz dazu sind nicht verifizierbare Typen Typen, deren Informationen zur Laufzeit gelöscht und nicht mehr verfügbar sind. Dies sind nur Generika – List<String> , List<Integer> usw.

Erinnern Sie sich übrigens, was Varargs sind ?

Falls Sie es vergessen haben: Dies sind Argumente variabler Länge. Sie sind in Situationen nützlich, in denen wir nicht genau wissen, wie viele Argumente an unsere Methode übergeben werden können. Wenn wir zum Beispiel eine Taschenrechnerklasse haben und diese eine Methode hat sum. Sie können der Methode sum()2 Zahlen, 3, 5 oder beliebig viele übergeben . Es wäre sehr seltsam, die Methode jedes Mal zu überladen, sum()um alle möglichen Optionen zu berücksichtigen. Stattdessen können wir Folgendes tun:
public class SimpleCalculator {

   public static int sum(int...numbers) {

       int result = 0;

       for(int i : numbers) {

           result += i;
       }

       return result;
   }

   public static void main(String[] args) {

       System.out.println(sum(1,2,3,4,5));
       System.out.println(sum(2,9));
   }
}
Konsolenausgabe:

15
11
Daher varargsweist die Verwendung in Kombination mit Generika einige wichtige Merkmale auf. Schauen wir uns diesen Code an:
import javafx.util.Pair;
import java.util.ArrayList;
import java.util.List;

public class Main {

   public static <E> void addAll(List<E> list, E... array) {

       for (E element : array) {
           list.add(element);
       }
   }

   public static void main(String[] args) {
       addAll(new ArrayList<String>(),  //  здесь все нормально
               "Leonardo da Vinci",
               "Vasco de Gama"
       );

       // а здесь мы получаем предупреждение
       addAll(new ArrayList<Pair<String, String>>(),
               new Pair<String, String>("Leonardo", "da Vinci"),
               new Pair<String, String>("Vasco", "de Gama")
       );
   }
}
Die Methode verwendet eine Liste und eine beliebige Anzahl von Objekten addAll()als Eingabe und fügt dann alle diese Objekte der Liste hinzu. In der Methode rufen wir unsere Methode zweimal auf . Beim ersten Mal fügen wir zwei reguläre Zeilen hinzu. Alles ist gut hier. Beim zweiten Mal fügen wir zwei Objekte hinzu . Und hier bekommen wir plötzlich eine Warnung: List<E>Emain()addAll()ListListPair<String, String>

Unchecked generics array creation for varargs parameter
Was bedeutet das? Warum erhalten wir eine Warnung und was hat das damit zu tun array? Array- Dies ist ein Array, und unser Code enthält keine Arrays! Beginnen wir mit dem zweiten. In der Warnung wird ein Array erwähnt, da der Compiler Argumente variabler Länge (varargs) in ein Array konvertiert. Mit anderen Worten, die Signatur unserer Methode lautet addAll():
public static <E> void addAll(List<E> list, E... array)
Es sieht tatsächlich so aus:
public static <E> void addAll(List<E> list, E[] array)
Das heißt, in der Methode main()konvertiert der Compiler unseren Code wie folgt:
public static void main(String[] args) {
   addAll(new ArrayList<String>(),
      new String[] {
        "Leonardo da Vinci",
        "Vasco de Gama"
      }
   );
   addAll(new ArrayList<Pair<String,String>>(),
        new Pair<String,String>[] {
            new Pair<String,String>("Leonardo","da Vinci"),
            new Pair<String,String>("Vasco","de Gama")
        }
   );
}
Mit dem Array ist alles in Ordnung String. Aber mit einem Array Pair<String, String>– nein. Tatsache ist, dass es Pair<String, String>sich hierbei um einen nicht verifizierbaren Typ handelt. Während der Kompilierung werden alle Informationen über Parametertypen (<String, String>) gelöscht. Das Erstellen von Arrays vom Typ Non-Reifiable Type ist in Java nicht zulässig . Sie können dies überprüfen, wenn Sie versuchen, manuell ein Array Pair<String, String> zu erstellen
public static void main(String[] args) {

   //  ошибка компиляции! Generic array creation
  Pair<String, String>[] array = new Pair<String, String>[10];
}
Der Grund liegt auf der Hand: Typensicherheit. Wie Sie sich erinnern, müssen Sie beim Erstellen eines Arrays angeben, welche Objekte (oder Grundelemente) dieses Array speichern soll.
int array[] = new int[10];
In einer der vorherigen Lektionen haben wir den Typlöschmechanismus ausführlich untersucht. In diesem Fall haben wir durch das Löschen von Typen die Information verloren, dass PairPaare in unseren Objekten gespeichert waren <String, String>. Das Erstellen eines Arrays ist unsicher. Denken Sie bei der Verwendung von Methoden mit varargsund Generika unbedingt an die Typlöschung und deren genaue Funktionsweise. Wenn Sie von dem von Ihnen geschriebenen Code absolut überzeugt sind und wissen, dass er keine Probleme verursachen wird, können Sie die varargsdamit verbundenen Warnungen mithilfe einer Annotation deaktivieren@SafeVarargs
@SafeVarargs
public static <E> void addAll(List<E> list, E... array) {

   for (E element : array) {
       list.add(element);
   }
}
Wenn Sie diese Anmerkung zu Ihrer Methode hinzufügen, wird die zuvor aufgetretene Warnung nicht angezeigt. Ein weiteres mögliches Problem bei der varargsgemeinsamen Verwendung von Generika ist die Haufenverschmutzung. Verwendung von Varargs bei der Arbeit mit Generika – 4In folgenden Situationen kann es zu einer Kontamination kommen:
import java.util.ArrayList;
import java.util.List;

public class Main {

   static List<String> makeHeapPollution() {
       List numbers = new ArrayList<Number>();
       numbers.add(1);
       List<String> strings = numbers;
       strings.add("");
       return strings;
   }

   public static void main(String[] args) {

       List<String> stringsWithHeapPollution = makeHeapPollution();

       System.out.println(stringsWithHeapPollution.get(0));
   }
}
Konsolenausgabe:

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
Vereinfacht ausgedrückt handelt es sich bei der Heap-Verschmutzung um eine Situation, in der sich Objekte vom Typ 1 auf dem Heap befinden sollten , Objekte des Typs jedoch aufgrund von Typsicherheitsfehlern Аdort landen . BIn unserem Beispiel passiert genau das. Zuerst haben wir eine Raw-Variable erstellt numbersund ihr eine generische Sammlung zugewiesen ArrayList<Number>. Danach haben wir die Nummer dort hinzugefügt 1.
List<String> strings = numbers;
In dieser Zeile versuchte der Compiler, uns vor möglichen Fehlern zu warnen, indem er die Warnung „ Nicht überprüfte Zuweisung... “ ausgab, aber wir ignorierten sie. Als Ergebnis haben wir eine generische Variable vom Typ List<String>, die auf eine generische Sammlung vom Typ verweist ArrayList<Number>. Diese Situation kann eindeutig zu Problemen führen! Das ist, was passiert. Mithilfe unserer neuen Variablen fügen wir der Sammlung einen String hinzu. Der Heap war verschmutzt – wir haben der typisierten Sammlung zuerst eine Zahl und dann eine Zeichenfolge hinzugefügt. Der Compiler warnte uns, aber wir ignorierten seine Warnung und erhielten ClassCastExceptionnur Ergebnisse, während das Programm ausgeführt wurde. Was hat es damit zu tun varargs? Die Verwendung varargsmit Generika kann leicht zu einer Haufenverschmutzung führen. Hier ist ein einfaches Beispiel:
import java.util.Arrays;
import java.util.List;

public class Main {

   static void makeHeapPollution(List<String>... stringsLists) {
       Object[] array = stringsLists;
       List<Integer> numbersList = Arrays.asList(66,22,44,12);

       array[0] = numbersList;
       String str = stringsLists[0].get(0);
   }

   public static void main(String[] args) {

       List<String> cars1 = Arrays.asList("Ford", "Fiat", "Kia");
       List<String> cars2 = Arrays.asList("Ferrari", "Bugatti", "Zaporozhets");

       makeHeapPollution(cars1, cars2);
   }
}
Was ist denn hier los? Aufgrund der Typlöschung sind unsere Parameterblätter (der Einfachheit halber nennen wir sie „Blätter“ statt „Listen“):
List<String>...stringsLists
- wird in ein Array von Blättern umgewandelt - List[]mit einem unbekannten Typ (vergessen Sie nicht, dass sich varargs durch die Kompilierung in ein reguläres Array verwandelt). Aus diesem Grund können wir Object[] arrayin der ersten Zeile der Methode problemlos eine Zuweisung zu einer Variablen vornehmen – die Typen wurden aus unseren Tabellen gelöscht! Und jetzt haben wir eine Variable vom Typ Object[], der wir alles hinzufügen können – alle Objekte in Java erben von Object! Im Moment haben wir nur eine Reihe von Saitenblättern. Aber dank der Verwendung varargsund Löschung von Typen können wir ihnen ganz einfach ein Zahlenblatt hinzufügen, was wir auch tun. Infolgedessen verschmutzen wir den Haufen, indem wir Objekte unterschiedlichen Typs mischen. Das Ergebnis wird die gleiche Ausnahme sein ClassCastException, wenn versucht wird, eine Zeichenfolge aus dem Array zu lesen. Konsolenausgabe:

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
Dies sind die unerwarteten Konsequenzen, die sich aus der Verwendung eines scheinbar einfachen Mechanismus ergeben können varargs:) Und hier endet unser heutiger Vortrag. Vergessen Sie nicht, ein paar Probleme zu lösen, und wenn Sie noch Zeit und Energie haben, studieren Sie zusätzliche Literatur. „ Effektives Java “ liest sich nicht von selbst! :) Auf Wiedersehen!
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION