2010-05-02 12 views
13

Gibt es einen praktischen Unterschied zwischen den folgenden Ansätzen zum Drucken aller Elemente in einem Bereich?Platzhalter und allgemeine Methoden

public static void printA(Iterable<?> range) 
{ 
    for (Object o : range) 
    { 
     System.out.println(o); 
    } 
} 

public static <T> void printB(Iterable<T> range) 
{ 
    for (T x : range) 
    { 
     System.out.println(x); 
    } 
} 

Offenbar printB ein zusätzliches beinhaltet prüfendes Objekt geworfen (siehe Zeile 16), der ich ziemlich dumm scheint - ist ein Objekt sowieso nicht alles?

public static void printA(java.lang.Iterable); 
    Code: 
    0: aload_0 
    1: invokeinterface #18, 1; //InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator; 
    6: astore_2 
    7: goto 24 
    10: aload_2 
    11: invokeinterface #24, 1; //InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object; 
    16: astore_1 
    17: getstatic #30; //Field java/lang/System.out:Ljava/io/PrintStream; 
    20: aload_1 
    21: invokevirtual #36; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V 
    24: aload_2 
    25: invokeinterface #42, 1; //InterfaceMethod java/util/Iterator.hasNext:()Z 
    30: ifne 10 
    33: return 

public static void printB(java.lang.Iterable); 
    Code: 
    0: aload_0 
    1: invokeinterface #18, 1; //InterfaceMethod java/lang/Iterable.iterator:()Ljava/util/Iterator; 
    6: astore_2 
    7: goto 27 
    10: aload_2 
    11: invokeinterface #24, 1; //InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object; 
    16: checkcast #3; //class java/lang/Object 
    19: astore_1 
    20: getstatic #30; //Field java/lang/System.out:Ljava/io/PrintStream; 
    23: aload_1 
    24: invokevirtual #36; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V 
    27: aload_2 
    28: invokeinterface #42, 1; //InterfaceMethod java/util/Iterator.hasNext:()Z 
    33: ifne 10 
    36: return 

Antwort

7

In Ihrem Beispiel wird der generische Typ in genau einer Stelle der Signatur verwendet. In diesem Szenario hat ein Typ T keinen Vorteil gegenüber einem Platzhalter für den Aufrufer. In Ihrem Beispiel hat der Typ auch keinen Vorteil für den Implementierer der Methode.

Ich finde die Wildcard-Version für einen Aufrufer einfacher zu verstehen, da es ausdrücklich "Ich interessiere mich überhaupt nicht für den Typ". In Ihrem Beispiel ist das checkcast in der Tat überflüssig. Es wäre erforderlich, wenn T wie in T extends Number begrenzt wurde. Dann ist ein Checkcast für Number erforderlich, weil die lokale Variable x vom Typ Number ist, aber die Iterator.next()-Methode gibt immer noch Object zurück. Sieht so aus, als ob der Java-Compiler sich nicht darum kümmert, die Besetzung zu optimieren. Das JIT wird dies wahrscheinlich zur Laufzeit tun.

UPDATE:

Wenn der generische Typ an mehreren Stellen verwendet wird, wie in cletus Antwort, Sie haben keine andere Wahl, als einen generischen Typen verwenden T oder auch den Compiler sehen keine Verbindung zwischen dem Parametertyp/return type (zwei beliebige Wildcards sind für den Compiler unterschiedlich).

Ein Grenzfall ist, wenn die Signatur nur den Typ an einer Stelle hat, aber die Implementierung benötigt einen generischen Typ anstelle eines Platzhalters. Denken Sie an eine void swap(List<T> list, int a, int b) Methode. Es muss Elemente aus der Liste entfernen und sie wieder einfügen. IIRC, Wirksames Java schlägt vor, eine öffentliche Methode mit dem Platzhalter und eine interne Hilfsmethode mit einem Typ zu verwenden, der die tatsächliche Implementierung enthält. Auf diese Weise erhält der Benutzer eine einfache API, und der Implementierer verfügt weiterhin über die Typsicherheit.

public void swap(List<?> list, int a, int b){ 
    swapHelper(list, a, b); 
} 
private <T> void swapHelper(List<T> list, int a, int b){ 
    ... 
} 
+0

Ich stimme zu: * In diesem Fall * ist die Wildcard-Version besser. – Jorn

2

Die zweite ist flexibler. Ein besseres Beispiel ist: Es sagt etwas über den Typ aus. Ob das für Sie nützlich ist oder nicht, hängt davon ab, was die Funktion tut.

Die zweite zeigt seinen Nutzen, wenn man etwas von der Methode zurückkehren will:

public static <T> List<T> reverse(List<T> list) { 
    for (int i=0; i<n/2; i++) { 
    T value = list.get(i); 
    list.set(i, list.get(list.size() - i - 1)); 
    list.set(list.size() - i = 1, value); 
    } 
    return list; 
} 

Es ist möglicherweise ein besseres Beispiel kopieren das Array aber den Punkt bleibt, dass Sie nicht wirklich die oben tun können, mit List<?> .

+0

Ich verstehe das nicht. Soweit ich das beurteilen kann, können die beiden Formen in genau denselben Kontexten verwendet werden, und man gibt nicht eine Art von Sicherheit mehr als die andere. Fehle ich etwas? –

+1

@Marcelo Wenn Sie die verwenden?Version würde die aufrufende Methode eine Liste zurückbekommen und diese Liste selbst in eine Liste umwandeln. Das ist ein bisschen unsicher, da der Compiler nicht überprüfen kann, ob die Implementierung tatsächlich eine Liste erzeugt. Der Vertrag (mit?) Besagt, dass eine Liste zurückgegeben werden soll, und das ist richtig, auch wenn es keine Liste ist. Die T-Version würde es dem Compiler somit ermöglichen, den Rückgabetyp zu überprüfen, und die aufrufende Methode wäre sicherer. – extraneon

+0

Ja, aber immer wenn das T nur an einer Stelle in der Argumentliste erscheint (wie im Beispiel des OP), kann es in einen Platzhalter umgewandelt werden. Dieses Beispiel ist anders (das T erscheint an zwei Stellen, einschließlich in der Rückkehr). – newacct

0

Nicht wirklich. Der resultierende Bytecode sollte praktisch identisch sein.

+0

Es ist nicht, siehe meine Bearbeitung. – fredoverflow

+0

Ah, ja. So überprüft die '' Version tatsächlich, dass der Inhalt vom richtigen Typ ist und daher fehlschlagen kann, wenn ein' Iterable 'etwas enthält, das kein' T' ist. Ich habe meine Antwort etwas weicher gemacht. –

+0

@Marcello: Nein es überprüft nicht, ob ein Iterable etwas enthält, das kein T ist. Es prüft nur, ob es etwas enthält, das nicht in Object umgewandelt werden kann. Aber Generika sind Java sind so dumm implementiert, dass ich annehmen würde, dass dies nur ein weiterer Rest ist ... – Foxfire

Verwandte Themen