2012-06-08 23 views
33

Ich bin ein Neuling in Allgemein und meine Frage ist: Was für einen Unterschied zwischen zwei Funktionen:Unterschied zwischen generischer Art und Wildcard-Typ

Funktion 1:

public static <E> void funct1 (List<E> list1) { 

} 

Funktion 2:

public static void funct2(List<?> list) { 

} 

Danke.

+1

Wenn String auf 'java.lang.String' verweist, denke ich, dass funct2 in den Wildcard-Ausdrücken recht redundant ist, da' java.lang.String' finale Klasse ist und nicht erweitert werden kann. – nhahtdh

+1

Das ist merkwürdig, weil Sie String technisch nicht erweitern können, da es eine letzte Klasse ist. –

+0

jedoch gibt es keine Einschränkung für die Verwendung von Funktion 2: wie es Unterklassen von String erlaubt (obwohl unmöglich) oder die String-Klasse selbst ... – ria

Antwort

29

Die erste Unterschrift sagt: list1 ist eine Liste von Es.

Die zweite Signatur sagt: Liste ist eine Liste von Instanzen eines Typs, aber wir kennen den Typ nicht.

Der Unterschied wird deutlich, wenn wir versuchen, die Methode zu ändern, so dass es ein zweites Argument, das in die Liste innerhalb der Methode hinzugefügt werden soll:

import java.util.List; 

public class Experiment { 
    public static <E> void funct1(final List<E> list1, final E something) { 
     list1.add(something); 
    } 

    public static void funct2(final List<?> list, final Object something) { 
     list.add(something); // does not compile 
    } 
} 

Die erste gut funktioniert. Und Sie können das zweite Argument nicht in etwas ändern, das tatsächlich kompiliert wird.

Eigentlich fand ich nur noch schöner Beweis für die Differenz:

public class Experiment { 
    public static <E> void funct1(final List<E> list) { 
     list.add(list.get(0)); 
    } 

    public static void funct2(final List<?> list) { 
     list.add(list.get(0)); // !!!!!!!!!!!!!! won't compile !!!!!!!!! 
    } 
} 

Man könnte als warum brauchen wir <?> wenn es schränkt nur das, was wir damit machen können (wie @Babu_Reddy_H in den Kommentaren tat) . Ich sehe die folgenden Vorteile der Wildcard-Version:

  • Der Anrufer weniger über das Objekt zu wissen, hat er in passiert zum Beispiel, wenn ich eine Karte von Listen haben. Map<String, List<?>> ich ihre Werte an Ihre Funktion übergeben können, ohne Angabe des Typs der Listenelemente. Also

  • Wenn ich so parametrisierte Objekte austeile, beschränke ich aktiv, was die Leute über diese Objekte wissen und was sie damit machen können (solange sie sich vom unsicheren Casting fernhalten).

Diese beiden ergeben Sinn, wenn ich sie kombiniere: List<? extends T>. Betrachten Sie zum Beispiel eine Methode List<T> merge(List<? extends T>, List<? extends T>), die die beiden Eingabelisten zu einer neuen Ergebnisliste zusammenführt. Sicher könnten Sie zwei weitere Typparameter einführen, aber warum sollten Sie das wollen? Es wäre zu viel, Dinge zu spezifizieren.

  • schließlich können Platzhalter untere Grenzen haben, so mit Listen können Sie die add Methode funktioniert, während get nicht geben Sie etwas Nützliches. Das löst natürlich die nächste Frage aus: Warum haben Generika keine unteren Grenzen?

Für eine eingehende Antwort siehe: When to use generic methods and when to use wild-card? und http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeArguments.html#FAQ203

+0

Danke, ich verstehe – Fio

+4

Es ist auch erwähnenswert, dass es nicht nur auf Puts ist. Mit einer 'List ' können Sie 'E obj = list.get (0)' schreiben, aber mit 'List ' können Sie nur 'Object obj = list.get (0)' schreiben. – yshavit

+0

@Jens schauder. Beide ? und E erlaubt nur Objektmethoden, auf dem Objekt aufzurufen. Und man kann zu der Liste hinzufügen und kann dies nicht mit der Liste tun. Warum brauchen wir dann überhaupt die Liste ? –

1

Liste als Parameter tuype besagt, dass der Parameter eine Liste von Elementen mit einem beliebigen Objekttyp sein muss. Darüber hinaus können Sie den Parameter E binden, um Verweise auf Listenelemente im Funktionskörper zu deklarieren.

Die Liste als Parametertyp hat dieselbe Semantik, außer dass es keine Möglichkeit gibt, Verweise auf die Elemente in der Liste zu deklarieren, außer Object zu verwenden. Andere Beiträge geben zusätzliche subtile Unterschiede.

+0

Sorry, ich habe einen Fehler in meiner Frage und ich habe die Frage bearbeitet. – Fio

+0

@Gene können Sie bitte erklären, warum im zweiten Funktionsparameter eine Liste von Objekten sein muss, die von String abgeleitet sind. Da Parameter seinen Objekttyp "Liste Liste" nicht definiert, denke ich, dass er jede Art von Objekt zusammen mit String enthalten kann. –

+0

Anstatt "zuerst" oder "zweite" zu sagen, fügen Sie den entsprechenden Code ein, um Verwechslungen zu vermeiden (und Bearbeitungen am OP). –

5

Generics macht die Sammlung typsicherer.

List<E>: E ist hier der Typ Parameter, die verwendet werden können, den Inhaltstyp der Liste, um zu bestimmen, aber es gab No Möglichkeit zu überprüfen, was der Inhalt während des runtime war.

Generics are checked only during compilation time. 

<? extends String>: Das war speziell baut in Java, um das Problem zu umgehen, die mit dem Typ Parameter war. "? extends String" bedeutet Liste

objects which IS-A String. 

Für zB haben:

Tier Klasse Dog-Klasse erweitert Tier Tiger Klasse Tier

Also mit "public void go(ArrayList<Animal> a)" wird NOT accept Hund oder Tiger, ihren Inhalt aber Tier erstreckt.

"public void go(ArrayList<? extends Animal> a)" ist was die ArrayList take in Dog and Tiger type.

prüfen Referenzen in Head First Java zu machen brauchte.

+0

kann ich nur verwenden? wie in obigem Beispiel gezeigt Liste Liste? dann welche Arten von Objekt kann –

+0

speichern "?" wird verwendet, um die Sammlung in die Lage zu versetzen, die Argumente von der aufgerufenen Methode zu übernehmen, die den hier erwähnten Typ "" erweitert. –

+0

Liste Liste in diesem Beispiel Benutzer hat den Typ nicht definiert ... welche Art von Objekt wird es dann nehmen? –

1

Die erste ist eine Funktion, die einen Parameter akzeptiert, die eine Liste von Elementen von E-Typ sein müssen.

der zweite Beispiel Typ nicht

definiert
List<?> list 

so können Sie die Liste von jeder Art von Objekten bestehen.

0

(Seit Ihrer Bearbeitung) Diese zwei Funktionssignaturen haben den gleichen Effekt wie externer Code - sie nehmen beide List als Argument. Ein Platzhalterzeichen entspricht einem Typparameter, der nur einmal verwendet wird.

1

ich erklären, in der Regel den Unterschied zwischen < E> und < ?> durch einen Vergleich mit logischen Quantifizierungen, dh universelle Quantifizierung und existentielle Quantifizierung.

  • zu "forall E, ..." entspricht
  • entspricht „gibt es etwas (durch), so dass ...."

daher beide der folgenden allgemeinen Methode Erklärungen

function 1:

public static <E> void funct1 (List<E>; list1) { 

} 

function 2:

public static void funct2(List<?> list) { 

} 

bedeutet, dass für alle Klassentyp E, wir Definieren Sie , und für einige existieren Klasse bezeichnet mit < ?>, dfine funct1.

0

Zusätzlich zu diesen Unterschieden bereits erwähnt, gibt es auch ein weiterer Unterschied: Sie können explizit die Typargumente für den Aufruf der generischen Methode gesetzt:

List<Apple> apples = ... 
ClassName.<Banana>funct2(apples); // for some reason the compiler seems to be ok 
           // with type parameters, even though the method has none 

ClassName.<Banana>funct1(apples); // compiler error: incompatible types: List<Apple> 
            //     cannot be converted to List<Banana> 

(ClassName ist der Name der Klasse enthält die Methoden.)

Verwandte Themen