Was ist ein Beispiel aus dem wirklichen Leben für Generika?

Lesezeit: 8 Minuten

Benutzer-Avatar
BanzaiTokio

ich verstehe das <? super T> repräsentiert jede Superklasse von T (Elternklasse von T auf jedem Niveau). Aber ich habe wirklich Mühe, mir ein echtes Beispiel für diesen generischen gebundenen Platzhalter vorzustellen.

Ich verstehe was <? super T> bedeutet und ich habe diese Methode gesehen:

public class Collections {
  public static <T> void copy(List<? super T> dest, List<? extends T> src) {
      for (int i = 0; i < src.size(); i++)
        dest.set(i, src.get(i));
  }
}

Ich suche ein Beispiel für Anwendungsfall aus dem wirklichen Leben wo diese Konstruktion verwendet werden kann und nicht für eine Erklärung dessen, was es ist.

  • Dies ist KEIN Duplikat, eine sehr berechtigte Frage

    – Eugen

    5. September 2018 um 13:12 Uhr

  • Ich glaube auch nicht, dass es ein Duplikat ist, er fragt nach konkreten Situationen, nicht nach dem Prinzip dahinter

    – ItFreak

    5. September 2018 um 13:28 Uhr

  • Dies ist der Anfang der Antwort, die ich gerade schreiben wollte, als diese geschlossen wurde: Ich stimme den nahen Wählern in gewissem Maße zu: Die Antwort könnte mit etwas Sorgfalt von stackoverflow.com/questions/2723397/… abgeleitet werden. Diese Frage (wie auch die Antworten hier) konzentriert sich jedoch auf das technische Prinzip. Eine einfache, realistisch Beispiel, wo es sinnvoll ist, es zu verwenden ? super T könnte hilfreich sein.

    – Marco13

    5. September 2018 um 14:49 Uhr

  • Denken Sie nicht, dass dies als Duplikat hätte geschlossen werden sollen, da der Autor eher nach realen Modellen von OOP fragt als nach ausführlichen Erklärungen, wie Vererbung in Java funktioniert.

    – Johannes Stark

    5. September 2018 um 14:49 Uhr

  • Ist dieses Beispiel hier nicht ein realer Anwendungsfall?

    – Benutzer253751

    6. September 2018 um 1:21 Uhr

Benutzer-Avatar
Eugen

Das einfachste Beispiel, das mir einfällt, ist:

public static <T extends Comparable<? super T>> void sort(List<T> list) {
    list.sort(null);
}

aus demselben genommen Collections. Auf diese Weise a Dog umsetzen kann Comparable<Animal> und wenn Animal setzt das schon um, Dog muss nichts machen.

EDIT für ein echtes Beispiel:

Nach einigem E-Mail-Ping-Pong darf ich ein reales Beispiel aus meinem Arbeitsplatz präsentieren (yay!).

Wir haben eine Schnittstelle namens Sink (es spielt keine Rolle, was es tut), die Idee ist, dass es ist sammelt sich an Dinge. Die Deklaration ist ziemlich trivial (vereinfacht):

interface Sink<T> {
    void accumulate(T t);
}

Offensichtlich gibt es eine Hilfsmethode, die a akzeptiert List und entwässert seine Elemente zu a Sink (es ist etwas komplizierter, aber um es einfach zu machen):

public static <T> void drainToSink(List<T> collection, Sink<T> sink) {
    collection.forEach(sink::accumulate);
}

Das ist einfach richtig? Brunnen…

Ich kann eine haben List<String>aber ich möchte es zu a ablassen Sink<Object> – Dies ist eine ziemlich übliche Sache für uns; aber das wird scheitern:

Sink<Object> sink = null;
List<String> strings = List.of("abc");
drainToSink(strings, sink);

Damit dies funktioniert, müssen wir die Deklaration ändern in:

public static <T> void drainToSink(List<T> collection, Sink<? super T> sink) {
    ....
}

  • Kotlin Lernprogramm Verwendet Source als Beispiel … was im Grunde das Dual von Ihnen ist Sink Beispiel.

    – Bakuriu

    5. September 2018 um 17:16 Uhr

  • Sicherlich könnten Sie es umgekehrt definieren, indem Sie die Liste erstellen? verlängert T?

    – Weckar E.

    5. September 2018 um 17:46 Uhr

  • @WeckarE. Nicht, wenn die Methode selbst auf der Liste steht.

    – Monika wieder einsetzen

    6. September 2018 um 1:35 Uhr

  • @WeckarE. Könnte ich ja, aber das würde die Semantik etwas verändern, dem kann ich jetzt nichts hinzufügen list, es wird nur ein Produzent; wie gesagt das ist ein vereinfachtes beispiel…

    – Eugen

    10. September 2018 um 9:14 Uhr


Benutzer-Avatar
Benoit

Angenommen, Sie haben diese Klassenhierarchie: Cat erbt von Mammal, das wiederum von Animal erbt.

List<Animal> animals = new ArrayList<>();
List<Mammal> mammals = new ArrayList<>();
List<Cat> cats = ...

Diese Aufrufe sind gültig:

Collections.copy(animals, mammals); // all mammals are animals
Collections.copy(mammals, cats);    // all cats are mammals
Collections.copy(animals, cats);    // all cats are animals
Collections.copy(cats, cats);       // all cats are cats 

Aber diese Anrufe sind nicht gültig:

Collections.copy(mammals, animals); // not all animals are mammals
Collections.copy(cats, mammals);    // not all mammals are cats
Collections.copy(cats, animals);    // mot all animals are cats

Die Methodensignatur stellt also einfach sicher, dass Sie von einer spezifischeren Klasse (unten in der Vererbungshierarchie) in eine allgemeinere Klasse (obere in der Vererbungshierarchie) kopieren und nicht umgekehrt.

  • Das super Das Schlüsselwort ist jedoch nicht erforderlich, damit dies funktioniert. Diese Signatur hätte auch identisches Verhalten aufgedeckt: public static <T> void copy(List<T> dest, List<? extends T> src) {

    – Nik

    6. September 2018 um 11:41 Uhr


  • @Nick Guter Fang. Warum dann diese Signatur? Diese Frage sollte Java-Sprachdesignern gestellt werden. Bisher ist der einzige Grund, den ich gefunden habe, dass Sie Code schreiben können wie: Collections.<Mammal>copy(animals, cats); – aber ich weiß nicht, warum jemand solchen Code schreiben würde/sollte …

    – Benoît

    6. September 2018 um 13:14 Uhr


Benutzer-Avatar
Oleksandr Pyrohov

Schauen Sie zum Beispiel in die Collections.addAll Methodenimplementierung:

public static <T> boolean addAll(Collection<? super T> c, T... elements) {
    boolean result = false;
    for (T element : elements)
        result |= c.add(element);
    return result;
}

Dabei können die Elemente in beliebige Collections eingefügt werden, deren Elementtyp ein Supertyp des Typs ist T des Elements.

Ohne einen nach unten begrenzten Platzhalter:

public static <T> boolean addAll(Collection<T> c, T... elements) { ... }

Folgendes wäre ungültig gewesen:

List<Number> nums = new ArrayList<>();
Collections.<Integer>addAll(nums , 1, 2, 3);

weil der begriff Collection<T> restriktiver ist als Collection<? super T>.


Ein anderes Beispiel:

Predicate<T> Schnittstelle in Java, die eine verwendet <? super T> Platzhalter in den folgenden Methoden:

default Predicate<T> and(Predicate<? super T> other);

default Predicate<T>  or(Predicate<? super T> other);

<? super T> ermöglicht die Verkettung einer größeren Bandbreite unterschiedlicher Prädikate, zum Beispiel:

Predicate<String> p1 = s -> s.equals("P");
Predicate<Object> p2 = o -> o.equals("P");

p1.and(p2).test("P"); // which wouldn't be possible with a Predicate<T> as a parameter

  • Ich finde dieses Beispiel nicht besonders überzeugend. Wenn Sie die explizite Instanziierung der Typvariablen weglassen, können Sie genauso gut schreiben Collections.addAll(nums, 1, 2, 3) mit jeder Methodensignatur.

    – Nik

    7. September 2018 um 1:55 Uhr

Benutzer-Avatar
xingbin

Angenommen, Sie haben eine Methode:

passToConsumer(Consumer<? super SubType> consumer)

dann rufst du diese Methode mit any auf Consumer was verbrauchen kann SubType:

passToConsumer(Consumer<SuperType> superTypeConsumer)
passToConsumer(Consumer<SubType> subTypeConsumer)
passToConsumer(Consumer<Object> rootConsumer)

Zum Beispiel:

class Animal{}

class Dog extends Animal{

    void putInto(List<? super Dog> list) {
        list.add(this);
    }
}

Also ich kann die setzen Dog hinein List<Animal> oder List<Dog>:

List<Animal> animals = new ArrayList<>();
List<Dog> dogs = new ArrayList<>();

Dog dog = new Dog();
dog.putInto(dogs);  // OK
dog.putInto(animals);   // OK

Wenn Sie sich ändern putInto(List<? super Dog> list) Methode zu putInto(List<Animal> list):

Dog dog = new Dog();

List<Dog> dogs = new ArrayList<>();
dog.putInto(dogs);  // compile error, List<Dog> is not sub type of List<Animal>

oder putInto(List<Dog> list):

Dog dog = new Dog();

List<Animal> animals = new ArrayList<>();
dog.putInto(animals); // compile error, List<Animal> is not sub type of List<Dog>

Ich habe ein Webradio geschrieben, also hatte ich die Klasse MetaInformationObject, die die Oberklasse für PLS- und M3U-Wiedergabelisten war. Ich hatte einen Auswahldialog, also hatte ich:

public class SelectMultipleStreamDialog <T extends MetaInformationObject>
public class M3UInfo extends MetaInformationObject
public class PLSInfo extends MetaInformationObject

Diese Klasse hatte eine Methode public T getSelectedStream().
Der Aufrufer erhielt also ein T, das vom konkreten Typ war (PLS oder M3U), aber an der Oberklasse arbeiten musste, also gab es eine Liste: List<T super MetaInformationObject>. wo das Ergebnis hinzugefügt wurde.
So könnte ein generischer Dialog die konkreten Implementierungen handhaben und der Rest des Codes könnte auf der Oberklasse funktionieren.
Hoffe das macht es etwas klarer.

Benutzer-Avatar
yschavit

Betrachten Sie dieses einfache Beispiel:

List<Number> nums = Arrays.asList(3, 1.2, 4L);
Comparator<Object> numbersByDouble = Comparator.comparing(Object::toString);
nums.sort(numbersByDouble);

Hoffentlich ist dies ein einigermaßen überzeugender Fall: Sie könnten sich vorstellen, die Zahlen zu Anzeigezwecken sortieren zu wollen (wofür der toString eine vernünftige Reihenfolge ist), aber Number ist selbst nicht vergleichbar.

Dies kompiliert, weil integers::sort nimmt ein Comparator<? super E>. Wenn es nur eine dauern würde Comparator<E> (wo E in diesem Fall ist Number), dann würde der Code nicht kompiliert werden, weil Comparator<Object> ist kein Subtyp von Comparator<Number> (Aus Gründen, die Sie Ihrer Frage nach bereits verstehen, werde ich nicht darauf eingehen).

Benutzer-Avatar
Peter Mortensen

Sammlungen dienen hier als gutes Beispiel.

Wie in 1 gesagt, List<? super T> lässt Sie erstellen List das enthält Elemente vom Typ, die weniger abgeleitet sind als Tsodass es Elemente enthalten kann, die von erben Tdas sind Art T und das T erbt von.

Auf der anderen Seite, List<? extends T> lässt Sie a definieren List die nur Elemente enthalten kann, die erben T (in einigen Fällen nicht einmal vom Typ T).

Dies ist ein gutes Beispiel:

public class Collections {
  public static <T> void copy(List<? super T> dest, List<? extends T> src) {
      for (int i = 0; i < src.size(); i++)
        dest.set(i, src.get(i));
  }
}

Hier möchten Sie projizieren List von weniger abgeleitetem Typ zu List von weniger abgeleitetem Typ. Hier List<? super T> versichert uns, dass alle Elemente aus src gelten in der neuen Kollektion.

1 : Unterschied zwischen und in Java

1195070cookie-checkWas ist ein Beispiel aus dem wirklichen Leben für Generika?

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

Privacy policy