Was ist der Unterschied zwischen ? und Objekt in Java-Generics?

Ich verwende Eclipse, um mir dabei zu helfen, etwas Code aufzuräumen, um Java-Generics richtig zu verwenden. Die meiste Zeit ist es eine ausgezeichnete Arbeit, um Typen zu folgern, aber es gibt Fälle, in denen der abgeleitete Typ so allgemein wie möglich sein muss: Objekt. Aber Eclipse scheint mir eine Option zu geben, zwischen einem Objekttyp und einem Typ “?” Zu wählen.

Also, was ist der Unterschied zwischen:

HashMap hash1; 

und

 HashMap hash2; 

   

Eine Instanz von HashMap entspricht Map , Nicht jedoch Map . Angenommen, Sie möchten eine Methode schreiben, die Maps von String s zu allem akzeptiert: Wenn Sie schreiben würden

 public void foobar(Map ms) { ... } 

Sie können keine HashMap . Wenn du schreibst

 public void foobar(Map ms) { ... } 

Es klappt!

Eine Sache, die manchmal in Java-Generika missverstanden wird, ist, dass List kein Subtyp von List . (Aber String[] ist eigentlich ein Subtyp von Object[] , das ist einer der Gründe, warum Generika und Arrays nicht gut mischen. (Arrays in Java sind kovariant, Generics sind nicht, sie sind invariant )).

Beispiel: Wenn Sie eine Methode schreiben möchten, die List von InputStream S und Subtypes von InputStream akzeptiert, würden Sie schreiben

 public void foobar(List< ? extends InputStream> ms) { ... } 

Übrigens: Joshua Blochs Effective Java ist eine hervorragende Ressource, wenn Sie die nicht so einfachen Dinge in Java verstehen möchten. (Ihre Frage ist auch im Buch sehr gut abgedeckt.)

Eine andere Möglichkeit, über dieses Problem nachzudenken, ist das

 HashMap hash1; 

ist äquivalent zu

 HashMap hash1; 

Verknüpfen Sie dieses Wissen mit dem “Get and Put-Prinzip” in Abschnitt (2.4) von Java Generics and Collections :

Das Get-and-Put-Prinzip: Verwenden Sie einen erweiterten Platzhalter, wenn Sie nur Werte aus einer Struktur abrufen, verwenden Sie einen Super-Platzhalter, wenn Sie nur Werte in eine Struktur einfügen, und verwenden Sie keinen Platzhalter.

und der Joker könnte hoffentlich mehr Sinn ergeben.

Es ist leicht zu verstehen, wenn Sie sich daran erinnern, dass Collection nur eine generische Sammlung ist, die Objekte vom Typ Object , aber Collection< ?> Ist ein Super-Typ für alle Arten von Sammlungen.

Sie können nichts sicher in Map , da Sie nicht wissen, welchen Typ die Werte haben sollen.

Sie können jedes Objekt in eine Map , da der Wert als Object .

Die Antworten oberhalb der Kovarianz decken die meisten Fälle ab, verpassen aber eins:

“?” enthält “Objekt” in der classnhierarchie. Man könnte sagen, String ist eine Art von Objekt und Objekt ist ein Typ von?. Nicht alles stimmt mit Objekt überein, aber alles stimmt überein?

 int test1(List< ?> l) { return l.size(); } int test2(List l) { return l.size(); } List< ?> l1 = Lists.newArrayList(); List l2 = Lists.newArrayList(); test1(l1); // compiles because any list will work test1(l2); // compiles because any list will work test2(l1); // fails because a ? might not be an Object test2(l2); // compiled because Object matches Object 

hash1 als HashMap hash1 HashMap hash1 dass die Variable hash1 eine beliebige HashMap , die einen Schlüssel von String und einen beliebigen hash1 hat.

 HashMap map; map = new HashMap(); map = new HashMap(); map = new HashMap(); 

Alles oben Genannte ist gültig, weil die Variable map eine dieser Hash-Maps speichern kann. Diese Variable kümmert sich nicht darum, was der Value-Typ der hashmap ist.

Mit einem Platzhalter können Sie jedoch keinen Objekttyp in Ihre Map einfügen. In der Tat können Sie mit der obigen Hash-Tabelle nichts mit der map Variablen einfügen:

 map.put("A", new Integer(0)); map.put("B", new Object()); map.put("C", "Some String"); 

Alle oben genannten Methodenaufrufe führen zu einem Fehler bei der Kompilierung, da Java nicht weiß, wie der Value-Typ der HashMap in der map ist.

Sie können immer noch einen Wert aus der Hash-Map erhalten. Obwohl Sie “den Typ des Werts nicht kennen” (weil Sie nicht wissen, welcher Typ von Hash-Map innerhalb Ihrer Variablen ist), können Sie sagen, dass alles eine Unterklasse von Object und, was auch immer Sie aus dem bekommen Karte wird vom Typ Objekt sein:

 HashMap myMap = new HashMap<>();// This variable is used to put things into the map. myMap.put("ABC", 10); HashMap map = myMap; Object output = map.get("ABC");// Valid code; Object is the superclass of everything, (including whatever is stored our hash map). System.out.println(output); 

Der obige Codeblock druckt 10 auf der Konsole.


Verwenden Sie zum Schluss eine HashMap mit Platzhaltern, wenn es Ihnen egal ist (dh es spielt keine Rolle), was die Typen der HashMap sind, zum Beispiel:

 public static void printHashMapSize(Map< ?, ?> anyMap) { // This code doesn't care what type of HashMap is inside anyMap. System.out.println(anyMap.size()); } 

Geben Sie andernfalls die erforderlichen Typen an:

 public void printAThroughZ(Map anyCharacterMap) { for (int i = 'A'; i < = 'Z'; i++) System.out.println(anyCharacterMap.get((char) i)); } 

Bei der obigen Methode müssen wir wissen, dass der Schlüssel der Map ein Character ist. Andernfalls würden wir nicht wissen, welcher Typ verwendet werden soll, um Werte davon zu erhalten. Alle Objekte verfügen jedoch über eine toString() -Methode, sodass die Map für ihre Werte einen beliebigen Objekttyp aufweisen kann. Wir können die Werte trotzdem ausdrucken.