Java 101: Die wichtigsten Java-Sprachfunktionen, Teil 5

Zurück 1 2 Page 2 Seite 2 von 2

Typinferenz und generische Konstruktoren für generische und nicht generische Klassen

Generische und nicht generische Klassen können generische Konstruktoren deklarieren, in denen ein Konstruktor eine formale Typparameterliste hat. Beispielsweise könnten Sie die folgende generische Klasse mit einem generischen Konstruktor deklarieren:

 public class Box { public  Box(T t) { // ... } } 

Diese Deklaration gibt eine generische Klasse Boxmit einem formalen Typparameter an E. Es gibt auch einen generischen Konstruktor mit formalen Typparametern an T. Sie können die generische Klasse instanziieren und ihren Konstruktor wie folgt aufrufen:

 new Box("Aggies") 

Dieser Ausdruck erstellt eine Instanz von Box, an Marbledie übergeben wird E. Außerdem leitet der Compiler das eigentliche Typargument Stringab T, da das Argument des Konstruktors ein StringObjekt ist.

Pre-Java 7-Compiler leiten die tatsächlichen Typargumente eines generischen Konstruktors ähnlich denen einer generischen Methode ab. Der Compiler von Java 7 kann jedoch auf die tatsächlichen Typargumente der generischen Klasse schließen, die in einem Diamond-Operator-Kontext instanziiert wird. Betrachten Sie das folgende Beispiel:

 Box box = new Box("Aggies"); 

Der Compiler leitet nicht nur den Typ Marblefür den formalen Typparameter Eder generischen Klasse ab Box, sondern leitet auch den Typ Stringfür den formalen Typparameter Tdes Konstruktors dieser generischen Klasse ab.

Projektmünze Kleingeld Nr. 8: Vereinfachter Aufruf der Varargs-Methode

Vor Java 7 führte jeder Versuch, eine varargs-Methode (variable Argumente, auch als variable Arität bezeichnet ) mit einem nicht überprüfbaren varargs-Typ aufzurufen, dazu, dass der Compiler eine Warnung "Unsichere Operation" ausgab. Um das Potenzial für viele ähnliche Warnmeldungen (eine pro Aufrufsite) auszuschließen, hat Java 7 die Warnung von der Aufrufsite in die Methodendeklaration verschoben.

Überprüfbare und nicht überprüfbare Typen

Ein überprüfbarer Typ macht zur Laufzeit seine vollständigen Typinformationen verfügbar . Beispiele hierfür sind primitive Typen, nicht generische Typen, Rohtypen und Aufrufe ungebundener Platzhalter. Im Gegensatz dazu werden bei einem nicht überprüfbaren Typ Typinformationen zur Kompilierungszeit durch Löschen des Typs entfernt, um die Binärkompatibilität mit Java-Bibliotheken und -Anwendungen sicherzustellen, die vor Generika erstellt wurden. Beispiele sind Setund Set. Da ein nicht überprüfbarer Typ zur Laufzeit nicht vollständig verfügbar ist, kann die JVM den Unterschied zwischen Setund nicht erkennen Set. Zur Laufzeit ist nur der Rohtyp Setverfügbar.

Generische Methoden, die vararg-Eingabeparameter enthalten, können eine Heap-Verschmutzung verursachen , bei der sich eine Variable eines parametrisierten Typs auf ein Objekt bezieht, das nicht zu diesem parametrisierten Typ gehört (z. B. wenn ein Rohtyp mit einem parametrisierten Typ gemischt wurde). Der Compiler meldet eine "ungeprüfte Warnung", da die Richtigkeit einer Operation mit einem parametrisierten Typ (wie einem Cast- oder Methodenaufruf) nicht überprüft werden kann.

Listing 13 zeigt die Haufenverschmutzung in einem Nicht-Varargs-Kontext.

Listing 13. Nachweis der Haufenverschmutzung in einem nicht varargs Kontext

 import java.util.Iterator; import java.util.Set; import java.util.TreeSet; public class HeapPollutionDemo { public static void main(String[] args) { Set s = new TreeSet(); Set ss = s; // unchecked warning s.add(new Integer(42)); // another unchecked warning Iterator iter = ss.iterator(); while (iter.hasNext()) { String str = iter.next(); // ClassCastException thrown System.out.println(str); } } } 

Variable sshat parametrisierten Typ Set. Wenn das java.util.Set, auf das verwiesen swird, zugewiesen ist ss, generiert der Compiler eine ungeprüfte Warnung. Dies liegt daran, dass der Compiler nicht feststellen kann, dass er ssich auf einen SetTyp bezieht (dies ist nicht der Fall). Das Ergebnis ist eine Haufenverschmutzung. (Der Compiler kann diese Zuordnung bewahren Rückwärtskompatibilität mit älteren Versionen , die Java generics nicht unterstützen. Weiterhin Typ Löschung Transformationen Setin Set, die Ergebnisse in einem Setzu einem anderen zugeordnet werden Set.)

Der Compiler generiert eine zweite ungeprüfte Warnung in der Zeile, die Setdie add()Methode aufruft . Dies geschieht, weil nicht festgestellt werden kann, ob sich eine Variable sauf einen Setoder einen SetTyp bezieht . Dies ist eine weitere Situation der Haufenverschmutzung. (Der Compiler kann diese Methode aufrufen , weil Lösch Transformationen Sets boolean add(E e)Methode boolean add(Object o), die zu dem Satz jeder Art von Objekt hinzufügen kann, einschließlich der java.lang.IntegerSubtyp java.lang.Object.)

Haufenverschmutzung kann leicht in einem varargs Kontext auftreten. Betrachten Sie beispielsweise Listing 14.

Listing 14. Demonstration der Haufenverschmutzung in einem Varargs-Kontext

 import java.util.Arrays; import java.util.List; public class UnsafeVarargsDemo { public static void main(String[] args) { unsafe(Arrays.asList("A", "B", "C"), Arrays.asList("D", "E", "F")); } static void unsafe(List... l) { Object[] oArray = l; oArray[0] = Arrays.asList(new Double(3.5)); String s = l[0].get(0); } } 

Die Object[] oArray = l;Aufgabe führt die Möglichkeit der Haufenverschmutzung ein. Ein Wert, der nicht mit dem parametrisierten Typ des varargs-Parameters übereinstimmt, lkann der Variablen zugewiesen werden oArray. Der Compiler generiert jedoch keine ungeprüfte Warnung, da dies bereits bei der Übersetzung List... lin geschehen ist List[] l. Diese Zuordnung ist gültig, da die Variable lden Typ hat List[], der Untertypen enthält Object[].

Außerdem gibt der Compiler keine Warnung oder keinen Fehler aus, wenn ein ListObjekt eines beliebigen Typs einer der oArrayArray-Komponenten zugewiesen wird. zum Beispiel oArray[0] = Arrays.asList(new Double(3.5));. Diese Zuweisung wird der ersten Array-Komponente oArrayeines ListObjekts zugewiesen, das ein einzelnes java.lang.DoubleObjekt enthält.

Die String s = l[0].get(0);Zuordnung ist problematisch. Das in der ersten Array-Komponente der Variablen gespeicherte Objekt lhat den Typ List, diese Zuweisung erwartet jedoch ein Objekt vom Typ List. Infolgedessen wirft die JVM java.lang.ClassCastException.

Kompilieren Sie diesen Quellcode ( javac -Xlint:unchecked UnsafeVarargsDemo.java). Sie sollten die folgende Ausgabe (aus Gründen der Lesbarkeit leicht neu formatiert) beachten, wenn Sie unter Java SE 7 Update 6 kompiliert werden:

 UnsafeVarargsDemo.java:8: warning: [unchecked] unchecked generic array creation for varargs parameter of type List[] unsafe(Arrays.asList("A", "B", "C"), ^ UnsafeVarargsDemo.java:12: warning: [unchecked] Possible heap pollution from parameterized vararg type List static void unsafe(List... l) ^ 2 warnings 

In meiner Java 101-Einführung in Generika habe ich angegeben, dass Sie in Array-Erstellungsausdrücken keine Typparameter verwenden können. Zum Beispiel können Sie nicht angeben elements = new E[size];. Der Compiler meldet eine Meldung "Fehler beim Erstellen eines generischen Arrays", wenn Sie dies versuchen. Es ist jedoch weiterhin möglich, ein generisches Array zu erstellen, jedoch nur in einem varargs-Kontext. Dies ist die Meldung der ersten Warnmeldung. Hinter den Kulissen verwandelt sich der Compiler List... lin List[] lund dann in List[] l.

Beachten Sie, dass die Warnung zur Heap-Verschmutzung an der unsafe()Deklarationsstelle der Methode generiert wird . Diese Nachricht wird nicht an der Aufrufsite dieser Methode generiert, was bei Java 5- und 6-Compilern der Fall ist.

Nicht alle Varargs-Methoden tragen zur Haufenverschmutzung bei. An der Deklarationsstelle der Methode wird jedoch weiterhin eine Warnmeldung ausgegeben. Wenn Sie wissen, dass Ihre Methode nicht zur Heap-Verschmutzung beiträgt, können Sie diese Warnung unterdrücken, indem Sie sie mit der @SafeVarargsAnnotation deklarieren. Java 7 hat den java.lang.SafeVarargsAnnotationstyp eingeführt . Da Arraysdie asList()Methode der Klasse beispielsweise nicht zur Verschmutzung des Haufens beitragen kann, wurde die Erklärung dieser Methode @SafeVarargswie folgt kommentiert :

 @SafeVarargs public static  List asList(T... a) 

Die @SafeVarargsAnnotation eliminiert die generischen Warnmeldungen zur Array-Erstellung und Heap-Verschmutzung. Es ist ein dokumentierter Teil des Vertrags der Methode und behauptet, dass die Implementierung der Methode den formalen Parameter varargs nicht falsch behandelt.

Abschließend

Java 7 verbesserte die Entwicklerproduktivität durch Einführung einer automatischen Ressourcenverwaltung über die Anweisung "Try-with-Resources" zusammen mit einer neuen AutoCloseableSchnittstelle, Einschaltzeichenfolge, Multi-Catch, endgültigem erneuten Löschen, binären Literalen, Unterstrichen in numerischen Literalen und Änderungen am Compilertyp Inferenzalgorithmus, der den sogenannten Diamantoperator einführte und den Aufruf der Varargs-Methode vereinfachte. Weiter oben in Java 101: Die Serie der nächsten Generation befasst sich mit den Sprachfunktionen von Lambda und der funktionalen Benutzeroberfläche von Java 8.

Diese Geschichte "Java 101: Die wesentliche Java-Sprachfeature-Tour, Teil 5" wurde ursprünglich von JavaWorld veröffentlicht.