1. Vorschlagen des Konzepts der Generika (warum werden Generika benötigt)?
Schauen wir uns zunächst den folgenden kurzen Code an:
public class generictest {public static void main (String [] args) {listlist = new ArrayList (); list.add ("qqyumidi"); list.add ("Mais"); list.add (100); für (int i = 0; i <list.size (); i ++) {String name = (string) list.get (i); // 1 System.out.println ("Name:" + Name); }}}Definieren Sie eine Sammlung von Listentypen, fügen Sie zuerst zwei Werte von String -Typen hinzu und fügen Sie dann einen ganzzahligen Wert hinzu. Dies ist vollständig zulässig, da der Standardtyp der Liste zu diesem Zeitpunkt Objekt ist. In der nachfolgenden Schleife ist es einfach, Fehler ähnlich wie // 1 zu haben, da ich vergessen habe, Ganzzahl -Werte oder andere Codierungsgründe in der Liste zuvor hinzuzufügen. Da die Zusammenstellungsphase normal ist, erscheinen die Ausnahme von "java.lang.ClassCastexception" zur Laufzeit. Daher sind solche Fehler während des Codierungsprozesses schwer zu erkennen.
Während des Codierungsprozesses wie oben stellten wir fest, dass es zwei Hauptprobleme gibt:
1. Wenn wir ein Objekt in die Sammlung einfügen, erinnert sich die Sammlung nicht an den Typ dieses Objekts. Wenn dieses Objekt erneut aus der Sammlung herausgenommen wird, wird der Kompilieryp des geänderten Objekts zum Objekttyp, aber sein Laufzeittyp ist immer noch ein eigener Typ.
2. Wenn Sie also das Sammelelement AT // 1 herausnehmen, müssen künstlich erzwungene Typen in den spezifischen Zieltyp konvertiert werden, und es ist leicht zu sehen, dass die Ausnahme von "java.lang.clascastexception" angezeigt wird.
Gibt es also eine Möglichkeit, die Sammlung an verschiedene Arten von Elementen in der Sammlung zu erinnern, und um dies zu erreichen, solange es während der Zusammenstellung kein Problem gibt, wird es während der Laufzeit keine "java.lang.classcastexception" -Ausnahme geben? Die Antwort ist, Generika zu verwenden.
2. Was ist ein Generikum?
Generika, dh "parametrisierter Typ". Wenn es um Parameter geht, ist das Vertraute am besten konkrete Parameter beim Definieren einer Methode und dann die tatsächlichen Parameter beim Aufrufen dieser Methode. Wie verstehen Sie also den Parametrisierungstyp? Wie der Name schon sagt, bedeutet dies, dass der Typ den Typ aus dem ursprünglichen spezifischen Typ parameterisiert wird, ähnlich den variablen Parametern in der Methode. Zu diesem Zeitpunkt wird der Typ auch als Parameterform (kann als Typ formaler Parameter bezeichnet werden) definiert, und dann wird der spezifische Typ (typischem Parameter) bei Verwendung/aufgerufen.
Es scheint ein bisschen kompliziert. Schauen wir uns zunächst das obige Beispiel anhand generisches Schreiben an.
public class generictest {public static void main (String [] args) { /* listlist = new ArrayList (); list.add ("qqyumidi"); list.add ("Mais"); list.add (100); */ List <string> list = new ArrayList <string> (); list.add ("qqyumidi"); list.add ("Mais"); //list.add(100); // 1 fordert einen Kompilierungsfehler auf (int i = 0; i <list.size (); i ++) {String name = list.get (i); // 2 System.out.println ("Name:" + Name); }}}Nach Verwendung generisches Schreiben tritt ein Kompilierungsfehler auf, wenn Sie ein ganzzahliges Objekt bei // 1 hinzufügen möchten. In der List <String> ist es direkt begrenzt, dass nur Elemente des String -Typs in der List -Sammlung enthalten sein können, sodass die Typen nicht an // 2 gegossen werden müssen, da die Sammlung zu diesem Zeitpunkt die Typinformationen des Elements erinnern kann und der Compiler bestätigen kann, dass es sich um einen String -Typ handelt.
Wenn wir die obige generische Definition kombinieren, wissen wir, dass in List <String> String ein Typparameter ist, dh die entsprechende List -Schnittstelle muss formale Parameter der Type enthalten. Darüber hinaus ist das Rückgabeergebnis der Get () -Methode direkt dieser formale Parametertyp (dh der entsprechende Parameter des eingehenden Typs). Schauen wir uns die spezifische Definition der Listenschnittstelle an:
öffentliche Schnittstellenliste <E> erweitert die Sammlung <E> {int size (); boolean isempty (); boolean enthält (Objekt O); Iterator <e> iterator (); Objekt [] toArray (); <t> t [] toArray (t [] a); boolean add (e e); boolean entfernen (Objekt O); boolean enthält alle (Sammlung <?> c); Boolean Addall (Sammlung <? Erweitert E> C); Boolean Addall (int Index, Sammlung <? Erweitert E> C); boolean removeall (Sammlung <?> C); booleschen Retainall (Sammlung <?> C); void clear (); boolean gleich (Objekt O); int HashCode (); E GET (INT INDEX); E set (int index, e element); void add (int Index, E -Element); E entfernen (int index); int indexof (Objekt O); int lastIndexof (Objekt O); ListIterator <E> listIterator (); ListIterator <E> ListIterator (int Index); List <E> Sublist (int vonIndex, int toIndex);};}Wir können sehen, dass nach der generischen Definition in der List -Schnittstelle e in <e> einen formalen Typ -Parameter darstellt, der bestimmte Typparameter empfangen kann. In dieser Schnittstellendefinition, in der E erscheint, bedeutet dies, dass die von außen akzeptierten Parameter der gleichen Typen akzeptiert werden.
Natürlich ist ArrayList eine Implementierungsklasse für die Listenschnittstelle, und das Definitionsformular lautet:
Aus diesem Grund verstehen wir aus der Sicht des Quellcode -Sichts, warum das Objekt des Ganzzahltyps bei // 1 falsch kompiliert wird und der bei // 2 erhaltene Typ direkt der String -Typ ist.
öffentliche Klasse ArrayList <E> erweitert AbstractList <E> implementiert die Liste <E>, randomaccess, klonbar, java.io.serializable {public boolean add (e) {sealEcapacityInternal (Größe + 1); // Inkrementiert modcount !! ElementData [Größe ++] = e; zurückkehren; } public e get (int index) {rangecheck (index); checkforComodification (); return arrayList.this.elementData (Offset + Index); } //...Omit andere spezifische Definitionsprozesse}3.. Passen Sie generische Schnittstellen, generische Klassen und generische Methoden an
Aus dem obigen Inhalt hat jeder den spezifischen Betriebsprozess von Generika verstanden. Es ist auch bekannt, dass Schnittstellen, Klassen und Methoden auch mit Generika definiert und entsprechend verwendet werden können. Ja, wenn es speziell verwendet wird, kann es in generische Schnittstellen, generische Klassen und generische Methoden unterteilt werden.
Benutzerdefinierte generische Schnittstellen, generische Klassen und generische Methoden ähneln im obigen Java -Quellcode der Liste und ArrayList. Wie folgt betrachten wir die einfachste generische Klasse und Methodendefinition:
public class generictest {public static void main (String [] args) {box <string> name = new box <string> ("corn"); System.out.println ("Name:" + name.getData ()); }} Klassenbox <T> {private t Data; public box () {} public box (t data) {this.data = data; } public t getData () {returndaten; }}Bei der Definition generischer Schnittstellen, generischen Klassen und generischen Methoden werden unsere gemeinsamen Parameter wie T, E, K, V usw. häufig verwendet, um generische formale Parameter darzustellen, da sie Typparameter empfangen, die von der externen Verwendung übergeben wurden. Sehen sich die Typen der entsprechenden Objektinstanzen für verschiedene Arten von eingehenden Parametern gleich?
public class generictest {public static void main (String [] args) {box <string> name = new box <string> ("corn"); Box <Ganzzahl> älter = new Box <Ganzzahl> (712); System.out.println ("Name Klasse:" + name.getClass ()); // com.qqyumidi.box System.out.println ("Altersklasse:" + ay.getClass ()); // com.qqyumidi.box System.out.println (name.getClass () == ay.getClass ()); // WAHR }}Daraus stellten wir fest, dass bei der Verwendung generischer Klassen unterschiedliche generische Argumente, obwohl sie in den wahren Sinne nicht übergeben werden, unterschiedliche Typen erzeugt werden. Es gibt nur eine generische Klasse, die in verschiedenen generischen Argumenten im Speicher passt, dh immer noch der ursprüngliche grundlegendste Typ (Box in diesem Beispiel). Natürlich können wir es logischerweise als mehrere verschiedene generische Typen verstehen.
Der Grund dafür ist, dass der Zweck des Generika -Konzepts in Java darin besteht, dass es nur in der Code -Kompilierungsphase funktioniert. Während des Zusammenstellungsprozesses werden die relevanten Informationen der Generika nach korrekter Überprüfung der generischen Ergebnisse gelöscht. Das heißt, die erfolgreich zusammengestellte Klassendatei enthält keine generischen Informationen. Generische Informationen geben nicht die Laufzeitphase ein.
Dies wird in einem Satz zusammengefasst: Generische Typen werden logisch als mehrere verschiedene Typen angesehen und sind tatsächlich die gleichen Grundtypen.
Vier. Geben Sie Platzhalter ein
Nach der obigen Schlussfolgerung wissen wir, dass Box <Nummer> und Box <Ganzzahl> tatsächlich beide Boxtypen sind. Jetzt müssen wir weiterhin eine Frage erkunden. Kann also logischerweise Box <Nummer> und Box <Ganzzahl> als generische Typen mit Eltern-Kind-Beziehungen angesehen werden?
Um dieses Problem zu klären, schauen wir uns weiterhin das folgende Beispiel an:
public class generictest {public static void main (String [] args) {box <number> name = new box <number> (99); Box <Ganzzahl> älter = new Box <Ganzzahl> (712); getData (Name); // Die Methode getData (Box <Nummer>) im Typ Generictest ist // nicht für die Argumente (Box <GanzEger>) getData (Alter) anwendbar; // 1} public static void getData (Box <Nummer> data) {System.out.println ("Daten:" + data.getData ()); }}Wir haben festgestellt, dass eine Fehlermeldung bei Code // 1: Die Methode getData (Box <Number>) im t ype generictest ist für die Argumente nicht anwendbar (Box <Ganzzahl>). Durch die Aufforderung von Informationen wissen wir natürlich, dass Box <Nummer> nicht logisch als übergeordnete Kastenklasse <Ganzzahl> angesehen werden kann. Was ist der Grund?
public class generictest {public static void main (String [] args) {box <Integer> a = neuer Box <Integer> (712); Box <Nummer> b = a; // 1 Box <float> f = neuer Box <float> (3.14F); B.SetData (f); // 2} public static void getData (Box <Nummer> data) {System.out.println ("Daten:" + data.getData ()); }} Klassenbox <T> {private t Data; public box () {} public box (t data) {setData (Daten); } public t getData () {returndaten; } public void setData (t data) {this.data = data; }}In diesem Beispiel gibt es eine Fehlermeldung bei // 1 und // 2. Hier können wir die kontersichere Methode verwenden, um sie zu erklären.
Unter der Annahme, dass Box <Nummer> logisch als übergeordnete Kastenklasse <Gefteger> angesehen werden kann, gibt es keine Fehlereingabeaufforderungen bei // 1 und // 2. Dann tritt das Problem auf. Welchen Typ ist es, wenn Daten über die GetData () -Methode abgerufen werden? Ganze Zahl? Schweben? oder Nummer? Darüber hinaus muss aufgrund der unkontrollierbaren Reihenfolge im Programmierungsprozess bei Bedarf ein Typurteil erfolgen und die Typumwandlung durchgeführt werden. Dies widerspricht natürlich der Idee der Generika, daher kann Box <Nummer> logischerweise nicht als übergeordnete Klassenklasse von Box <Ganzzahl> angesehen werden.
OK, dann schauen wir uns auf das erste Beispiel in "Wildcards Type Type" zurück, wir kennen den tieferen Grund für die spezifischen Fehleraufforderungen. Wie kann man es also lösen? Der Hauptquartier kann eine neue Funktion definieren. Dies steht offensichtlich im Gegensatz zum Polymorphismus -Konzept in Java. Daher benötigen wir einen Referenztyp, der logisch verwendet werden kann, um die übergeordnete Klasse von beiden Box <Ganzzahl> und Box <Nummer> darzustellen, und somit wurde der Typ Wildcard ins Leben gerufen.
Typ -Wildcards werden im Allgemeinen anstelle spezifischer Typargumente verwendet. Beachten Sie, dass dies ein Typparameter ist, kein Typparameter! Und Box <?> Ist logisch die übergeordnete Klasse aller Box <Ganzzahl>, Box <nummer> ... usw. Daher können wir immer noch generische Methoden definieren, um solche Anforderungen zu erfüllen.
public class generictest {public static void main (String [] args) {box <string> name = new box <string> ("corn"); Box <Ganzzahl> älter = new Box <Ganzzahl> (712); Box <Nummer> number = neuer Kästchen <Nummer> (314); getData (Name); getData (Alter); getData (Nummer); } public static void getData (Box <?> Daten) {System.out.println ("Daten:" + data.getData ()); }}Manchmal hören wir auch von den oberen und unteren Arten von Wildcards. Wie genau ist es?
Wenn Sie im obigen Beispiel eine Methode definieren müssen, die ähnlich wie getData () funktioniert, gibt es jedoch weitere Einschränkungen für Typargumente: Es kann nur die Zahlenklasse und ihre Unterklassen sein. Zu diesem Zeitpunkt ist die Obergrenze der Wildkarten vom Typ Wildcards erforderlich.
public class generictest {public static void main (String [] args) {box <string> name = new box <string> ("corn"); Box <Ganzzahl> älter = new Box <Ganzzahl> (712); Box <Nummer> number = neuer Kästchen <Nummer> (314); getData (Name); getData (Alter); getData (Nummer); // GetuppernumberData (Name); // 1 getuppernumberData (Alter); // 2 getuppernumberData (Nummer); // 3} public static void getData (Box <?> Data) {System.out.println ("Daten:" + data.getData ()); } public static void getuppernumberData (Box <? Erweitert Nummer> Daten) {System.out.println ("Daten:" + data.getData ()); }}Zu diesem Zeitpunkt wird natürlich der Anruf bei Code // 1 eine Fehlermeldung angezeigt, während der Anruf bei // 2 // 3 normal ist.
Die Obergrenze der Wildkarten vom Typ wird durch die Form von Kasten <? erweitert die Zahl>. Entsprechend ist die untere Grenze der Wildkarten vom Typ Wildcards die Form von Kasten <? Superzahl>, und seine Bedeutung ist genau das Gegenteil der Obergrenze der Typ -Wildcards. Ich werde es hier nicht zu viel erklären.
5. zusätzliches Kapitel
Die Beispiele in diesem Artikel werden hauptsächlich zur Veranschaulichung einiger Ideen in Generika zitiert und haben nicht unbedingt die praktische Benutzerfreundlichkeit. Wenn es um Generika geht, glaube ich außerdem, dass das, was Sie am meisten verwenden, in der Sammlung liegt. Tatsächlich können Sie im tatsächlichen Programmierungsprozess Generics verwenden, um die Entwicklung zu vereinfachen und die Qualität des Codes gut sicherzustellen. Und eine zu beachten ist, dass es in Java kein sogenanntes generisches Array gibt.
Für Generika ist es das Wichtigste, die Ideen und Zwecke dahinter zu verstehen.
Das obige ist eine Erstellung von Wissen und Informationen zu Java -Generika. Wir werden in Zukunft weiterhin relevante Informationen hinzufügen. Vielen Dank für Ihre Unterstützung für diese Website!