2009-10-03 17 views
5

Ich habe eine Schnittstelle A, die Klasse B implementiert.Java Generics Curiosity

Die folgende generische Methode funktioniert

public static <T, U extends T> List<T> listFactory(Collection<U> source) { 
    return new ArrayList<T>(source); 
} 

aber

public static <T> List<T> listFactory(Collection<? extends T> source) { 
    return new ArrayList<T>(source); 
} 

nicht (Übersetzungsfehler, Typ stimmt nicht überein), wenn ich die Ausgabe in

List<A> tester = listFactory(B.defaultCollectionFactory(3)); 

defaultCollectionFactory(int count) statisch leite bietet eine Sammlung von B s, mit ein Standardkennzeichnungsschema

Keine Erkenntnisse darüber, warum das so ist? Es sieht so aus, als würden das generische U und der Platzhalter dasselbe tun.

+1

Wie funktioniert es "nicht"? Was ist die Fehlermeldung? – newacct

+1

Sieht so aus, als hätten Sie unten zwei richtige Antworten für das, was das Problem verursacht - aber ich würde ihren Rat nicht wörtlich nehmen. Bleiben Sie bei der Nicht-Wildcard-Version, sodass der Aufrufer der Methode diese Begrenzung der Platzhalter nicht umgehen muss. –

+0

@newacct: Kompilierungsfehler, Typ stimmt nicht überein – Carl

Antwort

2

Im ersten Konstrukt geben Sie an, dass Sie eine List der Schnittstelle des übergebenen Elements zurückgeben. Sie geben die Beziehung zwischen dem übergebenen Objekt und dem Rückgabeobjekttyp in der U extends T-Richtung an. In diesem Fall kann der Compiler assoziiert A und B mit T und U ist.

In der zweiten gibt es keine solche Unterscheidung, so dass der Compiler davon ausgeht, dass T sich auf B bezieht und den Rückgabewert als List<B> eingeben wird. Sie fallen dann in die Falle, wo, obwohl B eine Instanz von A ist, List<B> ist keine Instanz von List<A>. Der Compiler wird sich beschweren:

Mismatch Typ: kann nicht aus Liste <B> konvertieren < A zur Liste >

Sie das finden wird, mit dem ersten Konstrukt, Sie haben die Freiheit, eine List Angabe von jeder Schnittstelle die B implementiert oder eine Oberklasse in der B Hierarchie (List<Object>, zum Beispiel), und der Compiler wird nicht beschweren.

+0

Also die Priorität (Art von) für Typ-Inferenz ist von Argumenten zuerst, dann Rückgabetyp für diese Art von Generika. – Carl

+0

gewählt auf der Grundlage einer guten Erklärung (die Erickson bietet auch), plus Vermeidung der Beratung der Klasse. Struktur, die ich denke, ist hässlich. – Carl

3

Der Compiler führt einen anderen Typparameter für die listFactory-Methode als Sie erwarten. Es folgert, dass T Typ ist B, so die Signatur effektiv List<B> listFactory(Collection<? extends B> source). Geben Sie den Typparameter A an, indem Sie im Methodenaufruf explizit angeben:

+0

@erikson: Dieser statische Aufruf auf einem parametrisierten Typ funktioniert tatsächlich, macht keinen Sinn für mich, können Sie es mit einem Beispiel und Theorie Erklärung, danke. –

+0

Jede statische Methode kann (wirklich, sollte) von der Klasse qualifiziert werden, zu der sie gehört. Das OP hat nicht angegeben, was es ist; Ich habe "Test" verwendet. Das würde Ihnen einen normalen statischen Methodenaufruf geben: 'Test.listFactory (...)'. Anschließend können die Typargumente für jede parametrisierte Methode explizit angegeben werden, indem sie vor dem Methodennamen hinzugefügt werden. Das ist die ''. Setze sie zusammen und du bekommst meine Antwort. – erickson