1. Was genau ist ein Generikum?
Bevor wir über den Typ -Inferenz diskutieren, müssen wir überprüfen, was generisch ist. Generika sind neue Funktionen von Java SE 1.5. Die Essenz von Generika ist ein parametrisierter Typ, dh der Datentyp wird als Parameter angegeben. In den Laiengründen wären "Typvariablen". Diese Art von Variable kann bei der Erstellung von Klassen, Schnittstellen und Methoden verwendet werden. Der einfachste Weg, Java Generics zu verstehen, besteht darin, es als eine bequeme Syntax zu betrachten, mit der Sie einige Vorgänge beim casting von Java -Typen sparen können:
Liste <Apfel> box = new ArrayList <Apfel> (); Box.Add (New Apple ()); Apple Apple = Box.get (0);
Der obige Code selbst hat sich klar ausgedrückt: Box ist eine List mit Apple -Objekten. Die get -Methode gibt eine Apple -Objektinstanz zurück, und dieser Prozess erfordert keine Typumwandlung. Es gibt keine Generika, der obige Code muss so geschrieben werden:
Apple Apple = (Apple) Box.get (0);
Natürlich sind Generika nicht so einfach wie das, was ich hier beschrieben habe, aber dies ist nicht der Protagonist unseres Tages. Studenten, die Generika nicht sehr gut verstehen, müssen die Lektionen ausgleichen. Natürlich sind die besten Referenzmaterialien immer noch die offiziellen Dokumente.
2. durch Generika verursachte Probleme (vor Java 7)
Der größte Vorteil von Generika besteht darin, dass sie die Art des Programms bieten und rückwärtskompatibel sein können. Es gibt jedoch auch Dinge, die Entwickler unglücklich machen. Die Art der Generika muss jedes Mal geschrieben werden, wenn sie definieren. Diese Anzeigespezifikation fühlt sich nicht nur ein bisschen ausführlich an, sondern vor allem auch sind viele Programmierer nicht mit den Generika vertraut, sodass sie in vielen Fällen keine korrekten Parameter angeben können. Jetzt färbt der Compiler automatisch die Parameter -Generika, die diese Situation verringern und die Code -Lesbarkeit verbessern können.
3.. Verbesserungen der Typableitung von Generika in Java 7
Um generische Typen in früheren Versionen von Java 7 zu verwenden, müssen generische Typen auf beiden Seiten hinzugefügt werden, wenn die Werte deklariert und zugewiesen werden. Zum Beispiel:
Karte <String, Integer> map = new HashMap <String, Integer> ();
Viele Menschen müssen zu Beginn der gleiche gewesen sein, und sie waren verwirrt: Habe ich den Parametertyp in der Variablenerklärung nicht deklariert? Warum muss ich es noch ausschreiben, wenn das Objekt initialisiert wird? Dies ist auch das, was Generika beschweren, wenn sie zum ersten Mal erschienen. Es ist jedoch erfreulich, dass sich Java zwar verbessert, die Designer jedoch auch den Java -Compiler verbessern, um es intelligenter und humanisierter zu machen. Hier ist unser Protagonist heute: Typ Pushdown ... na ja ... es ist keine Typ Derivation, das heißt, Typ -Inferenz. Wenn dieser Typ erscheint, kann er, wenn er den obigen Code schreibt, die Parametertypen gerne weglassen, wenn die Objektinstanziation instanziiert ist, und er wird wie folgt:
Karte <String, Integer> map = new HashMap <> ();
In dieser Aussage schließt der Compiler beim Instanziieren HashMap automatisch den generischen Typ auf der Grundlage des generischen Typs bei der variablen Deklaration ab. Achten Sie noch einmal unbedingt auf das "<>" hinter new HashMap . Nur durch Hinzufügen dieses "<>" bedeutet es, dass es sich um automatische Inferenz handelt, da es sich ansonsten um einen nicht generischen HashMap handelt, und es wird eine Warnaufforderung beim Kompilieren des Quellcodes mit dem Compiler angegeben. Dieses Paar Winkelklammern "<>" heißt im offiziellen Dokument "Diamond".
Die Typ-Ableitung zu diesem Zeitpunkt ist jedoch nicht vollständig (selbst ein halbfeindliches Produkt), da bei der Erstellung von generischen Instanzen in Java SE 7 nur begrenzt ist: Nur wenn der parametrisierte Typ des Konstruktors im Kontext erheblich deklariert ist, kann die Typinferenz verwendet werden, da sonst nicht funktioniert. Zum Beispiel: Das folgende Beispiel kann in Java 7 nicht korrekt kompiliert werden (kann jetzt in Java 8 kompiliert werden, da der generische Typ automatisch auf der Grundlage der Methodenparameter abgeleitet wird):
List <String> list = new ArrayList <> (); list.add ("a"); // Da AddAll erwartet, Parameter der Type -Sammlung zu erhalten <? Erweitert String> Die folgende Anweisung kann die Liste nicht übergeben.addall (New ArrayList <> ());4. Reevolution in Java 8
In der neuesten offiziellen Java -Dokumentation können wir die Definition der Typableitung sehen:
Typ -Inferenz ist die Fähigkeit eines Java -Compilers, jede Methodenaufruf und die entsprechende Deklaration zu betrachten, um das Typ -Argument (oder Argumente) zu bestimmen, der den Aufruf anwendbar macht. Der Inferenzalgorithmus bestimmt die Argumenten und falls verfügbar, den Typ, den das Ergebnis zugewiesen oder zurückgegeben wird. Schließlich versucht der Inferenzalgorithmus, den spezifischsten Typ zu finden, der mit allen Argumenten funktioniert.
Kurz gesagt, die Typableitung bezieht sich auf die Fähigkeit des Compilers, die erforderlichen Parametertypen basierend auf der von Ihnen aufgerufenen Methode und der entsprechenden Deklaration zu bestimmen. Und in der offiziellen Dokumentation wird auch ein Beispiel gegeben, um zu erklären:
static <t> t pick (t a1, t a2) {return a2; } Serialisierbar S = pick ("D", New ArrayList <String> ()); Hier kann der Compiler ableiten, dass der Typ des in pick -Methode übergebenen Parameters Serializable ist.
In früheren Java -Versionen müssen Sie Folgendes schreiben, wenn das obige Beispiel kompiliert werden kann:
Serialisierbar s = this. <Serialisierbar> pick ("D", New ArrayList <String> ());Der detaillierte Grund für das Schreiben ist im generischen Kapitel von Bruce Eckels Java -Programmierdacht (vierte Ausgabe) zu sehen. Natürlich basiert dieses Buch auf Java 6, und diese Version hat kein Konzept der Typableitung. Wenn man dies sieht, können viele Menschen die Kraft der Typableitung in der neuesten Version deutlich erkennen. Es beschränkt sich nicht mehr auf die Erklärung und den Instanziierungsprozess generischer Klassen, sondern auf Methoden mit generischen Parametern.
4.1 Typinferenz und generische Methoden
In Bezug auf die Typableitung und die generischen Methoden in der neuen Version gibt das Dokument auch ein etwas komplexeres Beispiel. Ich habe es hier gepostet. Das Prinzip ist das gleiche wie das obige Serializable Beispiel, daher werde ich nicht auf Details eingehen. Wenn Sie es konsolidieren möchten, können Sie einen Blick darauf werfen:
public class boxdemo {public static <u> void addBox (u u, java.util.list <box <u >> Boxen) {Box <U> box = new box <> (); Box.set (u); Boxen.Add (Box); } public static <U> void outputboxes (java.util.list <Box <u >> Boxen) {int counter = 0; für (Box <U> Box: Boxes) {u boxcontents = box.get (); System.out.println ("Box #" + counter + "enthält [" + boxcontents.toString () + "]"); Zähler ++; }} public static void main (String [] args) {java.util.ArrayList <Box <Integer >> listoFIntierboxes = new Java.util.ArrayList <> (); BoxDemo. <Ganzzahl> addBox (Integer.ValueOf (10), ListoFinTeGerboxes); BoxDemo.Addbox (Integer.ValueOf (20), ListoFInerGerboxes); BoxDemo.Addbox (Integer.ValueOf (30), ListoFInegerBoxes); BoxDemo.Outputboxes (ListoFInerGerboxes); }}Die obige Codeausgabe ist:
Box #0 enthält [10] Kasten Nr. 1 enthält [20] Box #2 enthält [30]
Lassen Sie mich erwähnen, dass der Fokus der generischen Methode addBox die Typbeschreibung liegt, die Sie in der Methode -Aufruf in der neuen Java -Version nicht mehr angezeigt müssen:
BoxDemo. <Ganzzahl> addBox (Integer.ValueOf (10), ListoFinTeGerboxes);
Der Compiler kann automatisch schließen, dass der Parametertyp von den in addBox übergebenen Parametern Integer ist.
4.2 Typinferenz und generische Konstrukteure generischer und nicht generischer Klassen
Nun ... Dies kann ein besserer Satz in Englisch sein: Typinferenz und generische Konstrukteure generischer und nicht generischer Klassen
Tatsächlich sind generische Konstruktoren keine Patente für generische Klassen. Nicht generische Klassen können auch ihre eigenen generischen Konstruktoren haben. Schauen Sie sich dieses Beispiel an:
Klasse MyClass <x> {<t> myClass (t t) {// ...}}Wenn die folgende Instanziierung an die MyClass -Klasse gemacht wird:
Neue MyClass <Ganzzahl> ("") OK, hier zeigen wir, dass der Parametertyp x von MyClass Integer ist, und für den Konstruktor stellt der Compiler fest, dass der formale Parameter t auf dem eingehenden String -Objekt String (""). Dies wurde in der Java7 -Version implementiert. Welche Verbesserungen wurden in Java8 vorgenommen? Nach Java8 können wir diese Instanziierung einer generischen Klasse mit einem generischen Konstruktor wie folgt schreiben:
MyClass <Ganzzahl> myObject = new MyClass <> (""); Ja, es ist immer noch das Paar Winkelklammern (<>), das als Diamond bezeichnet wird, so dass unser Compiler automatisch ableiten kann, dass die formalen Parameter X Integer und t String ist. Dies ist unserem ersten Beispiel für Map<String,String> sehr ähnlich, außer dass es eine Generalisierung des Konstruktors gibt.
Es ist zu beachten, dass die Typableitung nur basierend auf dem Parametertyp des Aufrufs abgeleitet werden kann, Zieltyp (diese werden in Kürze erörtert) und zurückgegeben (falls es eine Rückgabe vorliegt) und nicht basierend auf einigen Anforderungen nach dem Programm abgeleitet werden.
4.3 Zieltyp
Wie bereits erwähnt, kann der Compiler die Typableitung basierend auf dem Zieltyp durchführen. Der Zieltyp eines Ausdrucks bezieht sich auf den korrekten Datentyp, den der Compiler basierend auf dem Auftritt des Ausdrucks benötigt. Zum Beispiel dieses Beispiel:
static <T> list <T> leerlist (); list <string> listone = collections.EmptyList ();
Hier ist List <String> der Zieltyp, denn das, was hier benötigt wird, ist List<String> und Collections.emptyList() gibt List<T> zurück, sodass der Compiler hier färbt, dass T String sein muss. Dies ist in Java 7 und 8 in Ordnung. In Java 7 kann es jedoch in der folgenden Situation nicht normal zusammengestellt werden:
void ProcessStringList (Liste <string> StringList) {// Process StringList} ProcessStringList (CollectionS.EmptyList ());Zu diesem Zeitpunkt gibt Java7 diese Fehlermeldung an:
// List <Object> kann nicht in die Liste <string> konvertiert werden
Grund: Collections.emptyList() gibt List<T> zurück, und T hier erfordert t einen bestimmten Typ, aber da sie nicht aus der Methodeerklärung abgeleitet werden kann, dass das, was erforderlich ist, String ist, gibt der Compiler t einen Object an. Offensichtlich kann List<Object> nicht in List<String>. In der Java7 -Version müssen Sie diese Methode so aufrufen:
ProcessStringList (Sammlungen. <string> leerlist ());
In Java 8 ist es jedoch offensichtlich, dass der Compiler aufgrund der Einführung des Zieltyp -Konzepts List<String> (dh der Zieltyp hier) ist, sodass der Compiler das T in der zurückgegebenen List<T> String sein muss, sodass die Beschreibung der processStringList(Collections.emptyList()); ist in Ordnung.
Die Verwendung von Zieltypen ist in Lambda -Ausdrücken am offensichtlichsten.
Zusammenfassen
OK, die oben genannten sind einige persönliche Einblicke in die Typableitung in Java. Zusammenfassend ist es die zunehmend perfekte Typableitung, irgendeine Art von Umwandlungsarbeiten zu vervollständigen, die natürlich zu sein scheint, aber all diese Arbeiten werden dem Compiler für automatische Derivationen überlassen, anstatt es den Entwicklern zu ermöglichen, sie anzuzeigen. Ich hoffe, der Inhalt dieses Artikels wird für alle im Lernen von Java hilfreich sein. Wenn Sie Fragen haben, können Sie eine Nachricht zur Kommunikation überlassen.