JavaRush /Java-Blog /Random-DE /Was sind Generika in Java?

Was sind Generika in Java?

Veröffentlicht in der Gruppe Random-DE
Hallo! Heute werden wir über Generika sprechen. Ich muss sagen, dass Sie viel Neues lernen werden! Darüber hinaus werden die nächsten Vorträge den Generika gewidmet sein. Was sind Generika in Java - 1 Wenn Sie also an diesem Thema interessiert sind, haben Sie Glück: Heute erfahren Sie viel über die Eigenschaften von Generika. Wenn nicht, beruhigen Sie sich und entspannen Sie sich! :) Das ist ein sehr wichtiges Thema und Sie müssen es wissen. Beginnen wir mit einer einfachen Frage: „Was“ und „Warum“. Was sind Generika? Generics sind Typen mit einem Parameter. Beim Erstellen eines Generikums geben Sie nicht nur dessen Typ an, sondern auch die Art der Daten, mit denen es arbeiten soll. Ich denke, das offensichtlichste Beispiel ist Ihnen bereits in den Sinn gekommen – das ist ArrayList! So erstellen wir es normalerweise im Programm:
import java.util.ArrayList;
import java.util.List;

public class Main {

   public static void main(String[] args) {

       List<String> myList1 = new ArrayList<>();
       myList1.add("Test String 1");
       myList1.add("Test String 2");
   }
}
Wie Sie vielleicht erraten haben, besteht die Besonderheit der Liste darin, dass es nicht möglich ist, alles hineinzustopfen: Sie funktioniert ausschließlich mit Objekten String. Machen wir nun einen kurzen Ausflug in die Geschichte von Java und versuchen die Frage zu beantworten: „Warum?“ Dazu schreiben wir selbst eine vereinfachte Version der ArrayList-Klasse. Unsere Liste kann nur Daten zum internen Array hinzufügen und diese Daten empfangen:
public class MyListClass {

   private Object[] data;
   private int count;

   public MyListClass() {
       this.data = new Object[10];
       this.count = 0;
   }

   public void add(Object o) {
       this.data[count] = o;
       count++;
   }

   public Object[] getData() {
       return data;
   }
}
Nehmen wir an, wir möchten, dass unsere Liste nur Zahlen speichert Integer. Wir haben keine Generika. IntegerWir können die o-Instanz von check im nicht explizit angeben add(). Dann ist unsere gesamte Klasse nur für geeignet Integerund wir müssen für alle auf der Welt existierenden Datentypen dieselbe Klasse schreiben! Wir beschließen, uns auf unsere Programmierer zu verlassen und einfach einen Kommentar im Code zu hinterlassen, damit diese dort nichts Unnötiges hinzufügen:
//use it ONLY with Integer data type
public void add(Object o) {
   this.data[count] = o;
   count++;
}
Einer der Programmierer hat diesen Kommentar übersehen und versehentlich versucht, mit Zeichenfolgen gemischte Zahlen in die Liste einzufügen und dann deren Summe zu berechnen:
public class Main {

   public static void main(String[] args) {

       MyListClass list = new MyListClass();
       list.add(100);
       list.add(200);
       list.add("Lolkek");
       list.add("Shalala");

       Integer sum1 = (Integer) list.getData()[0] + (Integer) list.getData()[1];
       System.out.println(sum1);

       Integer sum2 = (Integer) list.getData()[2] + (Integer) list.getData()[3];
       System.out.println(sum2);
   }
}
Konsolenausgabe: 300 Ausnahme im Thread „main“ java.lang.ClassCastException: java.lang.String kann nicht in java.lang.Integer bei Main.main(Main.java:14) umgewandelt werden. Was ist das Schlimmste in dieser Situation? Weit davon entfernt, die Unaufmerksamkeit eines Programmierers zu sein. Das Schlimmste ist, dass der falsche Code an einer wichtigen Stelle in unserem Programm landete und erfolgreich kompiliert wurde . Jetzt werden wir den Fehler nicht in der Codierungsphase sehen, sondern erst in der Testphase (und das ist im besten Fall der Fall!). Das Beheben von Fehlern zu einem späteren Zeitpunkt in der Entwicklung kostet viel mehr – sowohl Geld als auch Zeit. Das ist genau der Vorteil von Generics: Eine generische Klasse ermöglicht es einem unglücklichen Programmierer, einen Fehler sofort zu erkennen. Der Code lässt sich einfach nicht kompilieren!
import java.util.ArrayList;
import java.util.List;

public class Main {

   public static void main(String[] args) {

       List<Integer> myList1 = new ArrayList<>();

       myList1.add(100);
       myList1.add(100);
       myList1.add("Lolkek");//Fehler!
       myList1.add("Shalala");//Fehler!
   }
}
Der Programmierer wird sofort „zur Besinnung kommen“ und sich sofort korrigieren. Übrigens mussten wir keine eigene Klasse erstellen, Listum diese Art von Fehler zu sehen. Entfernen Sie einfach die Typklammern ( <Integer>) aus einer regulären ArrayList!
import java.util.ArrayList;
import java.util.List;

public class Main {

   public static void main(String[] args) {

      List list = new ArrayList();

      list.add(100);
      list.add(200);
      list.add("Lolkek");
      list.add("Shalala");

       System.out.println((Integer) list.get(0) + (Integer) list.get(1));
       System.out.println((Integer) list.get(2) + (Integer) list.get(3));
   }
}
Konsolenausgabe: 300 Ausnahme im Thread „main“ java.lang.ClassCastException: java.lang.String kann nicht in java.lang.Integer bei Main.main(Main.java:16) umgewandelt werden. Das heißt, selbst mit „nativen“ Tools Java, Sie können diesen Fehler machen und eine unsichere Sammlung erstellen. Однако, если вставить этот Code в IDEa, мы увидим предупреждение: “ Unchecked call to add(E) as a member of raw type of java.util.List ” Нам подсказывают, что при добавлении Element в коллекцию без дженериков что-то может пойти nicht so. Aber was bedeutet der Begriff „Rohtyp“? Die wörtliche Übersetzung wird ziemlich genau sein – „ Rohtyp “ oder „ Dirty-Typ “. Raw typeist eine generische Klasse, aus der ihr Typ entfernt wurde. Mit anderen Worten, List myList1das ist Raw type. Das Gegenteil raw typeist generic typeeine generische Klasse (auch bekannt als class parameterized type), korrekt erstellt, mit einer Typspezifikation. Zum Beispiel, List<String> myList1. Sie haben vielleicht eine Frage: Warum ist die Verwendung überhaupt erlaubt raw types? Der Grund ist einfach. Die Entwickler von Java haben die Unterstützung in der Sprache belassen, raw typesum keine Kompatibilitätsprobleme zu verursachen. Zum Zeitpunkt der Veröffentlichung von Java 5.0 (Generika erschienen in dieser Version erstmals) war bereits viel Code mit geschrieben worden raw types. Daher besteht diese Möglichkeit auch heute noch. Wir haben Joshua Blochs Klassiker „Effective Java“ bereits mehrfach in Vorträgen erwähnt. Als einer der Schöpfer der Sprache hat er das Thema der Verwendung von raw typesund in dem Buch nicht außer Acht gelassen generic types. Was sind Generika in Java - 2Kapitel 23 dieses Buches hat einen sehr treffenden Titel: „Verwenden Sie keine Rohtypen in neuem Code.“ Das ist etwas, das Sie sich merken müssen. Wenn Sie generische Klassen verwenden, wandeln Sie diese niemals generic typein um raw type.

Typisierte Methoden

Mit Java können Sie einzelne Methoden eingeben und so sogenannte generische Methoden erstellen. Warum sind solche Methoden praktisch? Erstens, weil sie es Ihnen ermöglichen, mit verschiedenen Parametertypen zu arbeiten. Wenn dieselbe Logik sicher auf verschiedene Typen angewendet werden kann, ist eine generische Methode eine gute Lösung. Schauen wir uns ein Beispiel an. Nehmen wir an, wir haben eine Art Liste myList1. Wir wollen alle Werte daraus entfernen und alle freien Stellen mit einem neuen Wert füllen. So sieht unsere Klasse mit einer generischen Methode aus:
public class TestClass {

   public static <T> void fill(List<T> list, T val) {
       for (int i = 0; i < list.size(); i++)
           list.set(i, val);
   }

   public static void main(String[] args) {

       List<String> strings = new ArrayList<>();
       strings.add("Старая строка 1");
       strings.add("Старая строка 2");
       strings.add("Старая строка 3");

       fill(strings, "Новая строка");

       System.out.println(strings);

       List<Integer> numbers = new ArrayList<>();
       numbers.add(1);
       numbers.add(2);
       numbers.add(3);

       fill(numbers, 888);
       System.out.println(numbers);
   }
}
Achten Sie auf die Syntax, sie sieht etwas ungewöhnlich aus:
public static <T> void fill(List<T> list, T val)
Dem Rückgabetyp ist <T> vorangestellt, was auf eine generische Methode hinweist. In diesem Fall benötigt die Methode zwei Parameter als Eingabe: eine Liste von Objekten T und ein weiteres separates Objekt T. Durch die Verwendung von <T> wird die Typisierung der Methode erreicht: Wir können dort keine Liste von Zeichenfolgen und eine Zahl übergeben. Eine Liste mit Strings und einem String, eine Liste mit Zahlen und einer Zahl, eine Liste unserer Objekte Catund ein anderes Objekt Cat– das ist der einzige Weg. Die Methode main()zeigt deutlich, dass die Methode fill()problemlos mit verschiedenen Datentypen funktioniert. Als Eingabe werden zunächst eine Liste mit Zeichenfolgen und eine Zeichenfolge und dann eine Liste mit Zahlen und eine Zahl benötigt. Konsolenausgabe: [Newline, Newline, Newline] [888, 888, 888] Stellen Sie sich vor, fill()wir bräuchten Methodenlogik für 30 verschiedene Klassen und hätten keine generischen Methoden. Wir wären gezwungen, dieselbe Methode 30 Mal zu schreiben, nur für verschiedene Datentypen! Aber dank generischer Methoden können wir unseren Code wiederverwenden! :) :)

Typisierte Klassen

Sie können nicht nur die in Java bereitgestellten generischen Klassen verwenden, sondern auch eigene erstellen! Hier ist ein einfaches Beispiel:
public class Box<T> {

   private T t;

   public void set(T t) {
       this.t = t;
   }

   public T get() {
       return t;
   }

   public static void main(String[] args) {

       Box<String> stringBox = new Box<>();

       stringBox.set("Старая строка");
       System.out.println(stringBox.get());
       stringBox.set("Новая строка");

       System.out.println(stringBox.get());

       stringBox.set(12345);//ошибка компиляции!
   }
}
Unsere Klasse Box<T>(„Box“) ist getippt. Nachdem wir ihm bei der Erstellung einen Datentyp () zugewiesen haben <T>, können wir keine Objekte anderer Typen mehr darin platzieren. Dies ist im Beispiel zu sehen. Beim Erstellen haben wir angegeben, dass unser Objekt mit Strings arbeiten soll:
Box<String> stringBox = new Box<>();
Und wenn wir in der letzten Codezeile versuchen, die Zahl 12345 in das Feld einzufügen, erhalten wir einen Kompilierungsfehler! Ganz einfach haben wir unsere eigene generische Klasse erstellt! :) Damit ist unser heutiger Vortrag abgeschlossen. Aber wir verabschieden uns nicht von Generika! In den nächsten Vorlesungen werden wir über erweiterte Funktionen sprechen, also verabschieden Sie sich nicht! ) Viel Erfolg im Studium! :) :)
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION