Wann sollten Wildcards in Java Generics verwendet werden?

Lesezeit: 10 Minuten

Wann sollten Wildcards in Java Generics verwendet werden
Koray Tugay

das ist von HeadFirst-Java: ( Seite 575 )

Dies:

public <T extends Animal> void takeThing(ArrayList<T> list)

Macht das gleiche:

public void takeThing(ArrayList<? extends Animal> list)

Hier ist also meine Frage: Wenn sie genau gleich sind, warum schreiben wir nicht

public <? extends Animal> void takeThing(ArrayList<?> list)

oder

public void takeThing(ArrayList<T extends Animal> list)

Wann wäre es außerdem sinnvoll, ein ? anstelle eines T in einer Methodendeklaration (wie oben) mit Generics oder für eine Klassendeklaration? Was sind die Vorteile?

  • Wie sagt man public <? extends Animal> void takeThing(ArrayList<?> list)? Wenn Sie void sagen, gibt es keinen Rückgabetyp. Wenn Sie angeben, gibt es einen Rückgabetyp. Ich wusste nicht, dass Sie angeben können, ob Sie einen Rückgabetyp oder keinen Rückgabetyp haben möchten.

    – DevZer0

    23. Mai 2013 um 7:23 Uhr

  • stackoverflow.com/questions/13474791/… Dies dient als zukünftige Referenz für alle, die diese Frage sehen!

    – Koray Tugay

    23. Mai 2013 um 7:46 Uhr

Wann sollten Wildcards in Java Generics verwendet werden
Werzi2001

Der große Unterschied zw

public <T extends Animal> void takeThing(ArrayList<T> list)

und

public void takeThing(ArrayList<? extends Animal> list)

ist, dass Sie in der früheren Methode auf “T” innerhalb der Methode als die konkrete Klasse verweisen können, die angegeben wurde. Bei der zweiten Methode ist dies nicht möglich.

Hier ein komplexeres Beispiel zur Veranschaulichung:

// here i can return the concrete type that was passed in
public <T extends Animal> Map<T, String> getNamesMap(ArrayList<T> list) {
    Map<T, String> names = new HashMap<T, String>();
    for (T animal : list) {
        names.put(animal, animal.getName()); // I assume there is a getName() method
    }
    return names;
}

// here i have to use general Animal
public Map<Animal, String> getNamesMap(ArrayList<? extends Animal> list) {
    Map<Animal, String> names = new HashMap<Animal, String>();
    for (Animal animal : list) {
        names.put(animal, animal.getName()); // I assume there is a getName() method
    }
    return names;
}

Bei der ersten Methode erhalten Sie, wenn Sie eine Katzenliste übergeben, eine Karte mit der Katze als Schlüssel. Die zweite Methode würde immer eine Map mit allgemeinem Animal-Schlüssel zurückgeben.

Übrigens ist dies keine gültige Java-Syntax:

public <? extends Animal> void takeThing(ArrayList<?> list)

Bei dieser Form der generischen Methodendeklaration müssen Sie einen gültigen Java-Bezeichner verwenden und nicht “?”.

Bearbeiten:

Die Form „? erweitert den Typ“ gilt nur für Variablen- oder Parametertypdeklarationen. Innerhalb einer generischen Methodendeklaration muss es „Identifier extended Type“ sein, da Sie in Ihrer Methode auf den „Identifier“ verweisen können.

  • Der große Unterschied ist nicht, dass man sich nicht darauf beziehen kann? als Typ. Wenn das das einzige wäre? gut ist, dann wäre es völlig nutzlos.

    – Bennidi

    23. Mai 2013 um 8:13 Uhr

  • @bennidi: Welche großen Unterschiede siehst du sonst noch bei einer Methodendeklaration? Sie können in beiden Fällen die gleichen Parameter angeben. Die generische Methodendeklaration bietet Ihnen den Vorteil, auf den Typ zu verweisen, der eine höhere Typsicherheit ermöglicht (was definitiv nicht nutzlos ist).

    – Werzi2001

    23. Mai 2013 um 8:21 Uhr

  • Typsicherheit ist das, was Sie erhalten, wenn Sie einen Bezeichner verwenden und sich in Ihrer Methodensignatur darauf beziehen. Ich habe nicht gesagt, dass dies nutzlos ist. Ich sagte, dass “?” wäre nutzlos, wenn seine einzigen Fähigkeiten im Rest der Methode nicht referenzierbar wären. Der große Unterschied besteht darin, dass Sie nichts zu List< hinzufügen können? erweitert Tier>. Bitte sehen Sie, wie ich unten antworte.

    – Bennidi

    23. Mai 2013 um 8:47 Uhr

  • Ja, das ist richtig. Ich habe dieses Beispiel nur gemacht, um zu zeigen, dass T innerhalb der Methode verwendet werden kann. Es war nur ein Beispiel, das nicht direkt mit dem Beispiel in der Frage zusammenhängt (da er nach Unterschieden und Verwendungen fragte).

    – Werzi2001

    23. Mai 2013 um 14:20 Uhr

  • Für das Beispiel in der Frage besteht keine Notwendigkeit. Aber die Frage lautete: “Außerdem, wann wäre es sinnvoll, ein ? anstelle eines T in einer Methodendeklaration (wie oben) mit Generics oder für eine Klassendeklaration zu verwenden? Was sind die Vorteile?” und ich habe ein Beispiel gegeben.

    – Werzi2001

    24. Mai 2013 um 7:16 Uhr

1647094634 660 Wann sollten Wildcards in Java Generics verwendet werden
bennidi

Bei Wildcards geht es um Co/Contra-Varianz von Generika. Ich werde versuchen, an einigen Beispielen zu verdeutlichen, was das bedeutet.

Grundsätzlich hängt es damit zusammen, dass es sich bei den Typen S und T, wobei S ein Untertyp von T ist, um einen generischen Typ handelt G<S> ist kein gültiger Untertyp von G<T>

List<Number> someNumbers = new ArrayList<Long>(); // compile error

Abhilfe schaffen Sie mit Wildcards

List<? extends Number> someNumbers = new ArrayList<Long>(); // this works

Bitte beachten Sie, dass Sie nichts in eine solche Liste aufnehmen können

someNumbers.add(2L); //compile error

sogar (und für viele Entwickler überraschender):

List<? extends Long> someLongs = new ArrayList<Long>();
someLongs.add(2L); // compile error !!!

Ich denke, SO ist nicht der richtige Ort, um das im Detail zu diskutieren. Ich werde versuchen, einige der Artikel und Papiere zu finden, die dies ausführlicher erklären.

  • Übrigens: Ceylon hat eine sehr schöne Art, mit diesen Besonderheiten von Schriftsystemen umzugehen.

    – Bennidi

    23. Mai 2013 um 8:03 Uhr

  • Die Frage bezieht sich auf Parameter für eine Methode

    – neues Konto

    23. Mai 2013 um 21:32 Uhr

  • Dies ist die einzige Frage an der Tafel, die die Frage tatsächlich beantwortet. Die anderen sagen nur, dass gebundene Typen Platzhaltern überlegen sind.

    – Rap

    12. Juni 2014 um 18:09 Uhr

  • Danke @Rap. Co und Contra Variance werden nicht allgemein verstanden und scheinen die Menschen (mich eingeschlossen) ständig zu verwirren. Ich denke, das Typsystem von Java bietet auch nicht die beste Lösung. Ceylon oder Scala bieten zum Beispiel viel mehr Mechanismen, um mit generischer Polymorphie umzugehen. Übrigens, stimme der Antwort zu 🙂

    – Bennidi

    2. Juli 2014 um 7:57 Uhr

1647094634 581 Wann sollten Wildcards in Java Generics verwendet werden
Markus Peters

Das Binden des Typs an einen Typparameter kann leistungsfähiger sein, je nachdem, was die Methode tun soll. Ich bin mir nicht sicher, was takeThing tun soll, aber stellen Sie sich im Allgemeinen vor, wir hätten eine Methode mit einer dieser Typsignaturen:

public <T extends Animal> void foo(ArrayList<T> list);

//or

public void foo(ArrayList<? extends Animal> list);

Hier ist ein konkretes Beispiel für etwas, das Sie nur mit dem tun können Erste Typ Unterschrift:

public <T extends Animal> void foo(ArrayList<T> list) {
    list.add(list.remove(0)); // (cycle front element to the back)
} 

In diesem Fall T ist erforderlich, um den Typprüfer darüber zu informieren, dass das Element ist ENTFERNT aus der Liste ist ein OK-Element an addieren zur Liste.

Sie könnten dies nicht mit einem Platzhalter tun, da der Platzhalter, da er nicht an einen Typparameter gebunden ist, nicht nachverfolgt wird (naja, er wird durch “Captures” nachverfolgt, aber er kann nicht genutzt werden). Weitere Informationen dazu finden Sie in einer anderen Antwort, die ich gegeben habe: Wie funktionieren Generika von Generika?

  • “von etwas, das man nur mit der ersten Typsignatur machen kann” Stimmt nicht. Ich kann eine Methode der zweiten Signatur die Methode der ersten Signatur aufrufen lassen. Dann “erledigen” sie für jeden externen Code die gleiche Aufgabe.

    – neues Konto

    23. Mai 2013 um 21:24 Uhr


  • @newacct: Ok, das ist ein bisschen pingelig. Sie brauchen noch den Typparameter, nur nicht von außen sichtbar. In dem von mir zitierten Beitrag gehe ich detailliert darauf ein, wie eine generische Hilfsmethode Ihnen helfen kann, die Wildcard-Erfassung zu “entfesseln”. Wenn Sie dies in diesem Fall tun, werden die Dinge nur unnötig kompliziert, ohne dass ein erkennbarer Vorteil entsteht.

    – Markus Peters

    23. Mai 2013 um 23:13 Uhr


  • Angenommen, Sie schreiben eine API. Sie würden immer die zweite Signatur verwenden. Ob Sie intern mit der ersten Signatur ein anderes Verfahren verwenden, ist ein Implementierungsdetail, das nicht nach außen preisgegeben werden sollte. Der Punkt ist, dass in diesem Fall der Typparameter nicht dazu dient, dem Aufrufer irgendwelche Einschränkungen aufzuerlegen, da er nur an einer Stelle in Parametern verwendet wird. Sein Zweck ist völlig intern. Es sollte also intern bleiben. Morgen ändern Sie vielleicht die Methode so, dass der Parameter nicht mehr benötigt wird; Die Methodensignatur sollte sich nicht ändern müssen.

    – neues Konto

    24. Mai 2013 um 0:52 Uhr


  • @newacct: Ich bin nicht anderer Meinung, aber Sie stimmen mich ab, als ob es um die Frage nach dem besten Ansatz für die Auswahl einer API-Signatur ginge, wenn es darum ging, welche Funktionalität eine gegenüber der anderen bietet. Mein Hauptpunkt ist immer noch genau richtig. Obwohl Sie möglicherweise eine Ebene der Indirektion einführen und intern verbergen, geben Sie dennoch zu, dass Sie schließlich eine Methode mit der ersten Signatur benötigen, wie ich bereits erwähnt habe. Sie können die Arbeit nicht nur mit der zweiten Unterschrift erledigen.

    – Markus Peters

    24. Mai 2013 um 2:11 Uhr


Wenn du schreibst ? extends T Sie sagen “alles, was ein T oder genauer ist”. Zum Beispiel: a List<Shape> kann nur haben Shapes darin, während a List<? extends Shape> haben kann ShapeS, CircleS, Rectangles usw.

Wenn du schreibst ? super T Sie sagen “alles, was ein T oder allgemeiner ist”. Dies wird seltener verwendet, hat aber seine Anwendungsfälle. Ein typisches Beispiel wäre ein Rückruf: Wenn Sie eine übergeben möchten Rectangle zurück zu einem Rückruf, den Sie verwenden können Callback<? super Rectangle>seit einem Callback<Shape> wird umgehen können Rectangles auch.

Hier ist den entsprechenden Wikipedia-Artikel.

1647094635 187 Wann sollten Wildcards in Java Generics verwendet werden
dcernahoschi

Wenn dein takeThing -Methode muss Elemente zu hinzufügen list -Parameter wird die Wildcard-Version nicht kompiliert.

Der interessante Fall ist, wenn Sie nichts zur Liste hinzufügen und beide Versionen zu kompilieren und zu funktionieren scheinen.

In diesem Fall würden Sie die Wildcard-Version schreiben, wenn Sie verschiedene Tierarten in der Liste zulassen möchten (mehr Flexibilität), und die Parameterversion, wenn Sie eine feste Tierart in der Liste benötigen: den T-Typ.

Zum Beispiel die java.util.Collection erklärt:

interface Collection<E> {
  ...
  public boolean containsAll(Collection<?> c);
  ...
}

Und angenommen, Sie haben den folgenden Code:

Collection<Object> c = Arrays.<Object>asList(1, 2); 
Collection<Integer> i = Arrays.<Integer>asList(1, 2, 3); 
i.containsAll(c); //compiles and return true as expected

Wenn die java.util.Collection wäre:

interface Collection<E> {
    ...
    public boolean containsAll(Collection<E> c);
    ...
}

Der obige Testcode würde nicht kompilieren und die Flexibilität des Collection API würde reduziert werden.

Es ist erwähnenswert, dass die letztere Definition von containsAll hat den Vorteil, dass zur Kompilierzeit mehr Fehler abgefangen werden, zum Beispiel:

Collection<String> c = Arrays.asList("1", "2"); 
Collection<Integer> i = Arrays.asList(1, 2, 3); 
i.containsAll(c); //does not compile, the integer collection can't contain strings

Verfehlt aber den gültigen Test mit a Collection<Object> c = Arrays.<Object>asList(1, 2);

  • “Wenn Ihre takeThing-Methode dem Listenparameter Elemente hinzufügen muss, wird die Wildcard-Version nicht kompiliert.” Aber wo wirst du eine finden T trotzdem ergänzen?

    – neues Konto

    23. Mai 2013 um 21:27 Uhr

  • @newacct Sie könnten versuchen, über eine hinzuzufügen Animal oder Animal Unterklassenreferenz.

    – dcernahoschi

    24. Mai 2013 um 4:58 Uhr

  • aber dann wird es kein T

    – neues Konto

    24. Mai 2013 um 9:08 Uhr

  • Es wird nicht einmal kompiliert. @dcernahoschi

    – Koray Tugay

    8. Juli 2014 um 17:02 Uhr

1647094635 361 Wann sollten Wildcards in Java Generics verwendet werden
Rahul Saini

Die Verwendung von Java Generics Wildcards unterliegt dem GET-PUT-Prinzip (das auch als IN-OUT-Prinzip bekannt ist). Darin heißt es:
Verwenden Sie einen “erweiterten” Platzhalter, wenn Sie nur Werte aus einer Struktur erhalten, verwenden Sie einen “Super”-Platzhalter, wenn Sie nur Werte in eine Struktur einfügen, und verwenden Sie keine Platzhalter, wenn Sie beides tun. Dies gilt nicht für den Rückgabetyp einer Methode. Verwenden Sie keinen Platzhalter als Rückgabetyp. Siehe Beispiel unten:

public static<T> void copyContainerDataValues(Container<? extends T> source, Container<? super T> destinationtion){
destination.put(source.get());
}

  • “Wenn Ihre takeThing-Methode dem Listenparameter Elemente hinzufügen muss, wird die Wildcard-Version nicht kompiliert.” Aber wo wirst du eine finden T trotzdem ergänzen?

    – neues Konto

    23. Mai 2013 um 21:27 Uhr

  • @newacct Sie könnten versuchen, über eine hinzuzufügen Animal oder Animal Unterklassenreferenz.

    – dcernahoschi

    24. Mai 2013 um 4:58 Uhr

  • aber dann wird es kein T

    – neues Konto

    24. Mai 2013 um 9:08 Uhr

  • Es wird nicht einmal kompiliert. @dcernahoschi

    – Koray Tugay

    8. Juli 2014 um 17:02 Uhr

993900cookie-checkWann sollten Wildcards in Java Generics verwendet werden?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy