2014-09-11 25 views
5

ich in den Java-Tutorial durch den folgenden Abschnitt lese: http://docs.oracle.com/javase/tutorial/java/generics/capture.htmlJava Generics Wildcard vs typisierten Generics Nutzung

Es beginnt damit, dass der folgende Code erzeugt einen Fehler aufgrund der Tatsache, dass eine Erfassung kann nicht konvertiert werden ein Objekt, so dass die set Methode nicht, dass das Objekt bestätigen kann, ist vom Typ Capture # 1:

import java.util.List; 

public class WildcardError { 

    void foo(List<?> i) { 
     i.set(0, i.get(0)); 
    } 
} 

ich etwas die Überlegung dahinter verstehen. i.get gibt ein Object zurück, und der Compiler kann nicht feststellen, ob das Objekt vom Typ capture # 1 ist, so dass es nicht typsicher mit dem zweiten Argument übereinstimmen kann.

Es empfiehlt dann den folgenden Code mit dieser Methode funktioniert:

public class WildcardFixed { 

    void foo(List<?> i) { 
     fooHelper(i); 
    } 


    // Helper method created so that the wildcard can be captured 
    // through type inference. 
    private <T> void fooHelper(List<T> l) { 
     l.set(0, l.get(0)); 
    } 

} 

ich etwas verstehen, warum dieser Code auch in diesem Code funktioniert, ist l.get garantiert vom Typ T sein, so es kann als ein Argument vom Typ T.

Was ich nicht verstehe, ist vergangen, warum Sie nicht nur eine Methode, wie diese verwendet werden könnten, ohne Helfer:

class GenericsTest { 
    static <K> void bar(List<K> l) { 
     l.set(0, l.get(l.size() - 1)); 
    } 

    public static void main(String[] args) { 
     List<Integer> lst = Arrays.asList(1, 2, 3, 4); 
     bar(lst); 
     System.out.println(lst); // [4, 3, 2, 4] 
    } 
} 

Wenn Sie also Typinferenz verwenden, warum verwenden Sie nicht explizit typisierte Generika über einen Platzhalter und eine Hilfsfunktion? Hat die Verwendung von Platzhaltern in diesem Szenario Vorteile? In welchen Fällen würden Sie es vorziehen, einen Platzhalter für ein typisiertes Generikum zu verwenden?

+0

Was bedeutet 'static void' für Sie? –

+0

@Mike Für mich bedeutet das, dass K ein Typparameter ist, der nur für den Umfang der Methode gilt. Java verwendet Typinferenz, um herauszufinden, was K zur Laufzeit sein soll. In dem gezeigten Beispiel übergebe ich eine Instanz der Liste , so dass der Compiler K zu Integer führt. K kann ein beliebiger Referenztyp sein, und Referenztypen erstrecken sich von Object aus. Daher betrachte ich K in diesem Fall explizit als eine andere Art, explizit zu schreiben. – Shashank

+0

@Mike Ich weiß es nicht ... es scheint das gleiche wie eine regelmäßige Void-Methode zu sein. Ich wusste nicht, dass K irgendetwas mit der Rückkehr zu tun hatte. Ich benutze nur die Syntax von hier: http://docs.oracle.com/javase/tutorial/java/generics/methods.html – Shashank

Antwort

1

Zunächst ist zu beachten, dass void foo(List<?> i) und void <K> foo(List<K> i) beide genau die gleiche Reihe von Argumenten akzeptieren - Sie unter der Annahme nicht explizit ein K auf dem gattungsgemäßen Verfahren Fall angeben, jedes Argument, das auf eine Funktion Unterschrift übergeben werden kann, an denen übergeben werden anderes und umgekehrt. Also zu "außerhalb des Codes" sind beide Signaturen gleichermaßen nützlich.

Da sie äquivalent sind, ist die, die weniger Typparameter hat, einfacher und sollte immer bevorzugt werden. Wenn Sie eine öffentliche API für externen Code bereitstellen, sollten Sie sie immer in der einfachsten Form präsentieren und nur den minimalen Typ haben, der für die Sicherheit erforderlich ist. Der Typ List<?> reicht aus, um auszudrücken, dass er jede Art von List annehmen kann, also sollten wir das verwenden.

Wir verwenden zufällig den Typparameter K intern, um einige generische Probleme zu lösen, aber das ist nur ein unglückliches internes Implementierungsdetail, das äußerer Code nicht interessieren muss. Daher sollten wir diese Hässlichkeit verstecken und sie in eine schönere Funktionssignatur einfügen, wenn wir eine öffentliche API erstellen.

Zusätzlich zu den Abstraktionszwecken ist ein weiterer Grund, warum Sie eine bestimmte Signatur möchten, eine Überklassenmethode, die diese Signatur aufweist. Sie können beim Überschreiben keine Typparameter hinzufügen.

0

Ich denke, das Beispiel, das Sie im Tutorial erwähnen, bezieht sich auf die Behandlung von Fällen, wenn Sie Parameter mit ungebundenen Wildcards akzeptieren müssen <?>. Dies kann der Fall sein, wenn Sie mit Legacy-Code interagieren oder eine vorhandene Klasse mit einem solchen Wildcard-Typ erweitern.

Meistens werden Sie Dinge wie <T extends U> oder <? extends T> verwenden, wenn Sie Flexibilität benötigen, aber nicht auf die Typüberprüfung verzichten möchten.