Warum sind die Entfernungsmethoden von Java Collections nicht generisch?

Lesezeit: 8 Minuten

Warum sind die Entfernungsmethoden von Java Collections nicht generisch
Chris Mazzola

Warum nicht Collection.remove(Objekt o) generisch?

Sieht aus als ob Collection<E> könnte haben boolean remove(E o);

Wenn Sie dann versehentlich versuchen, (zum Beispiel) Set<String> statt jedem einzelnen String von a Collection<String>wäre es ein Kompilierzeitfehler und kein späteres Debugging-Problem.

  • Das kann dich wirklich beißen, wenn du es mit Autoboxing kombinierst. Wenn Sie versuchen, etwas aus einer Liste zu entfernen und ihr eine Ganzzahl anstelle eines Int übergeben, wird die Methode remove(Object) aufgerufen.

    – ScArcher2

    19. September 2008 um 19:51 Uhr

  • Ähnliche Frage zur Karte: stackoverflow.com/questions/857420/…

    – AlikElzin-kilaka

    4. September 2012 um 12:37 Uhr


Warum sind die Entfernungsmethoden von Java Collections nicht generisch
Meister

Josh Bloch und Bill Pugh beziehen sich auf dieses Problem in Java Puzzlers IV: The Phantom Reference Menace, Attack of the Clone und Revenge of The Shift.

Josh Bloch sagt (6:41), dass sie versucht haben, die get-Methode von Map, remove-Methode und einige andere zu generieren, aber “es hat einfach nicht funktioniert”.

Es gibt zu viele sinnvolle Programme, die nicht generiert werden könnten, wenn man als Parametertyp nur den generischen Typ der Collection zulässt. Das von ihm gegebene Beispiel ist ein Schnittpunkt von a List von Numbers und a
List von LongS.

  • Warum add() einen typisierten Parameter annehmen kann, remove() jedoch nicht, entzieht sich meinem Verständnis. Josh Bloch wäre die definitive Referenz für Sammlungsfragen. Es könnte alles sein, was ich bekomme, ohne zu versuchen, eine ähnliche Sammlungsimplementierung zu erstellen und mich selbst davon zu überzeugen. 🙁 Danke.

    – Chris Mazzola

    22. September 2008 um 17:34 Uhr

  • Chris – lesen Sie das Java Generics Tutorial PDF, es wird erklären, warum.

    – JeeBee

    27. Januar 2009 um 13:01 Uhr

  • Eigentlich ist es ganz einfach! Wenn add() ein falsches Objekt nehmen würde, würde es die Sammlung zerstören. Es würde Dinge enthalten, die es nicht enthalten sollte! Dies ist bei remove() oder contains() nicht der Fall.

    – Kevin Bourrillion

    7. November 2009 um 3:46 Uhr

  • Übrigens wird diese Grundregel, Typparameter nur zu verwenden, um tatsächlichen Schaden an der Sammlung zu verhindern, in der gesamten Bibliothek absolut konsequent befolgt.

    – Kevin Bourrillion

    7. November 2009 um 3:49 Uhr

  • @KevinBourrillion: Ich arbeite seit Jahren mit Generika (sowohl in Java als auch in C #), ohne jemals zu bemerken, dass es überhaupt eine Regel für den “tatsächlichen Schaden” gibt … aber jetzt, wo ich sie direkt angegeben habe, macht sie 100% vollkommen Sinn. Danke für den Fisch!!! Nur fühle ich mich jetzt gezwungen, zurückzugehen und meine Implementierungen zu betrachten, um zu sehen, ob einige Methoden degeneriert werden können und sollten. Seufzen.

    – corlettk

    22. August 2013 um 23:41 Uhr

1646308633 342 Warum sind die Entfernungsmethoden von Java Collections nicht generisch
neues Konto

remove() (in Map sowie im Collection) ist nicht generisch, da Sie in der Lage sein sollten, jeden Objekttyp an zu übergeben remove(). Das entfernte Objekt muss nicht vom gleichen Typ sein wie das Objekt, an das Sie übergeben remove(); es erfordert nur, dass sie gleich sind. Aus der Spezifikation von remove(), remove(o) entfernt das Objekt e so dass (o==null ? e==null : o.equals(e)) ist true. Beachten Sie, dass nichts erforderlich ist o und e vom gleichen Typ sein. Dies ergibt sich daraus, dass die equals() Methode nimmt an Object als Parameter, nicht nur vom gleichen Typ wie das Objekt.

Obwohl es im Allgemeinen zutreffen kann, dass viele Klassen dies haben equals() so definiert, dass seine Objekte nur Objekten seiner eigenen Klasse gleich sein können, ist dies sicherlich nicht immer der Fall. Zum Beispiel die Spezifikation für List.equals() sagt, dass zwei List-Objekte gleich sind, wenn sie beide Listen sind und denselben Inhalt haben, auch wenn sie unterschiedliche Implementierungen von sind List. Um auf das Beispiel in dieser Frage zurückzukommen, ist es möglich, a zu haben Map<ArrayList, Something> und für mich anzurufen remove() mit einer LinkedList als Argument, und es sollte den Schlüssel entfernen, der eine Liste mit demselben Inhalt ist. Dies wäre nicht möglich, wenn remove() waren generisch und schränkten ihren Argumenttyp ein.

  • Aber wenn Sie die Map als Map (anstelle von ArrayList) definieren würden, wäre es möglich gewesen, sie mit einer LinkedList zu entfernen. Ich denke, diese Antwort ist falsch.

    – AlikElzin-kilaka

    4. September 2012 um 12:21 Uhr


  • Die Antwort scheint richtig, aber unvollständig zu sein. Es bietet sich nur an zu fragen, warum zum Teufel sie das nicht generisch gemacht haben equals() Methode auch? Ich könnte mehr Vorteile für die Typsicherheit anstelle dieses “libertären” Ansatzes sehen. Ich denke, die meisten Fälle mit der aktuellen Implementierung sind eher für Fehler, die in unseren Code gelangen, als für die Freude an dieser Flexibilität, die die remove() Methode bringt.

    – kellogs

    5. Februar 2015 um 18:17 Uhr


  • @kellogs: Was meinst du mit “Genericize the equals() Methode”?

    – neues Konto

    5. Februar 2015 um 20:12 Uhr

  • @MattBall: “wo T die deklarierende Klasse ist” Aber es gibt keine solche Syntax in Java. T muss als Typparameter für die Klasse deklariert werden, und Object hat keine Typparameter. Es gibt keine Möglichkeit, einen Typ zu haben, der sich auf “die deklarierende Klasse” bezieht.

    – neues Konto

    27. März 2015 um 21:50 Uhr

  • Ich denke, Kellogs sagt, was wäre, wenn Gleichheit eine generische Schnittstelle wäre Equality<T> mit equals(T other). Dann hättest du remove(Equality<T> o) und o ist nur ein Objekt, das mit einem anderen verglichen werden kann T.

    – Westen

    11. Februar 2017 um 16:48 Uhr


Warum sind die Entfernungsmethoden von Java Collections nicht generisch
Bob Gettys

Denn wenn Ihr Typparameter ein Platzhalter ist, können Sie keine generische Entfernungsmethode verwenden.

Ich meine mich zu erinnern, dass ich mit der get(Object)-Methode von Map auf diese Frage gestoßen bin. Die get-Methode ist in diesem Fall nicht generisch, obwohl sie vernünftigerweise erwarten sollte, dass ein Objekt desselben Typs wie der erste Typparameter übergeben wird. Mir wurde klar, dass es keine Möglichkeit gibt, mit dieser Methode ein Element aus der Map herauszubekommen, wenn Sie Maps mit einem Platzhalter als erstem Typparameter weitergeben, wenn dieses Argument generisch war. Wildcard-Argumente können nicht wirklich erfüllt werden, da der Compiler nicht garantieren kann, dass der Typ korrekt ist. Ich vermute, dass add generisch ist, weil von Ihnen erwartet wird, dass Sie garantieren, dass der Typ korrekt ist, bevor Sie ihn der Sammlung hinzufügen. Wenn jedoch beim Entfernen eines Objekts der Typ falsch ist, wird es sowieso nicht übereinstimmen. Wenn das Argument ein Platzhalter wäre, wäre die Methode einfach unbrauchbar, obwohl Sie möglicherweise ein Objekt haben, von dem Sie GARANTIEREN können, dass es zu dieser Sammlung gehört, weil Sie gerade in der vorherigen Zeile einen Verweis darauf erhalten haben ….

Ich habe es wahrscheinlich nicht sehr gut erklärt, aber es erscheint mir logisch genug.

  • Könnten Sie das etwas näher erläutern?

    – Thomas Owens

    19. September 2008 um 19:29 Uhr

1646308634 274 Warum sind die Entfernungsmethoden von Java Collections nicht generisch
Hosam Ali

Neben den anderen Antworten gibt es noch einen weiteren Grund, warum die Methode an akzeptieren sollte Object, was Prädikate sind. Betrachten Sie das folgende Beispiel:

class Person {
    public String name;
    // override equals()
}
class Employee extends Person {
    public String company;
    // override equals()
}
class Developer extends Employee {
    public int yearsOfExperience;
    // override equals()
}

class Test {
    public static void main(String[] args) {
        Collection<? extends Person> people = new ArrayList<Employee>();
        // ...

        // to remove the first employee with a specific name:
        people.remove(new Person(someName1));

        // to remove the first developer that matches some criteria:
        people.remove(new Developer(someName2, someCompany, 10));

        // to remove the first employee who is either
        // a developer or an employee of someCompany:
        people.remove(new Object() {
            public boolean equals(Object employee) {
                return employee instanceof Developer
                    || ((Employee) employee).company.equals(someCompany);
        }});
    }
}

Der Punkt ist, dass das Objekt an die übergeben wird remove Methode ist für die Definition von verantwortlich equals Methode. Das Erstellen von Prädikaten wird auf diese Weise sehr einfach.

Angenommen, man hat eine Sammlung von Catund einige Objektreferenzen von Typen Animal, Cat, SiameseCatund Dog. Abfragen der Auflistung, ob sie das Objekt enthält, auf das durch die verwiesen wird Cat oder SiameseCat Referenz erscheint sinnvoll. Abfragen, ob es das Objekt enthält, auf das durch die verwiesen wird Animal Referenz mag zwielichtig erscheinen, aber es ist immer noch vollkommen vernünftig. Das fragliche Objekt könnte schließlich a sein Catund kann in der Sammlung erscheinen.

Auch wenn das Objekt zufällig etwas anderes als a ist Cat, gibt es kein Problem zu sagen, ob es in der Sammlung erscheint – antworten Sie einfach mit „nein, tut es nicht“. Eine Sammlung irgendeines Typs im “Lookup-Stil” sollte in der Lage sein, Verweise auf beliebige Supertypen sinnvoll zu akzeptieren und festzustellen, ob das Objekt in der Sammlung existiert. Wenn die übergebene Objektreferenz von einem nicht verwandten Typ ist, kann die Sammlung sie auf keinen Fall enthalten, sodass die Abfrage in gewissem Sinne nicht sinnvoll ist (sie wird immer mit „nein“ antworten). Da es jedoch keine Möglichkeit gibt, Parameter auf Subtypen oder Supertypen zu beschränken, ist es am praktischsten, einfach jeden Typ zu akzeptieren und für alle Objekte, deren Typ nichts mit dem der Sammlung zu tun hat, mit “Nein” zu antworten.

  • “Wenn die übergebene Objektreferenz von einem nicht verwandten Typ ist, kann die Sammlung ihn auf keinen Fall enthalten” Falsch. Es muss nur etwas Gleiches enthalten; und Objekte verschiedener Klassen können gleich sein.

    – neues Konto

    29. Mai 2012 um 8:24 Uhr

  • “mag zweifelhaft erscheinen, aber es ist immer noch vollkommen vernünftig” Ist es? Stellen Sie sich eine Welt vor, in der ein Objekt eines Typs nicht immer auf Gleichheit mit einem Objekt eines anderen Typs überprüft werden kann, da parametrisiert wird, welchem ​​Typ es gleich sein kann (ähnlich wie Comparable ist parametrisiert für die Typen, mit denen Sie vergleichen können). Dann wäre es nicht vernünftig, Leuten zu erlauben, etwas Nichtverwandtes zu passieren.

    – neues Konto

    29. Mai 2012 um 8:31 Uhr


  • @newacct: Es gibt einen grundlegenden Unterschied zwischen Größenvergleich und Gleichheitsvergleich: gegebene Objekte A und B eines Typs und X und Y eines anderen, so dass A>Bund X>Y. Entweder A>Y und Y<Aoder X>B und B<X. Diese Beziehungen können nur bestehen, wenn die Größenvergleiche beide Typen kennen. Im Gegensatz dazu kann sich die Gleichheitsvergleichsmethode eines Objekts einfach selbst für ungleich mit irgendetwas anderem Typ erklären, ohne irgendetwas über den betreffenden anderen Typ wissen zu müssen. Ein Objekt vom Typ Cat vielleicht keine Ahnung ob es…

    – Superkatze

    29. Mai 2012 um 15:15 Uhr

  • …”größer als” oder “kleiner als” ein Objekt des Typs FordMustangaber es sollte keine Schwierigkeiten haben zu sagen, ob es einem solchen Objekt gleich ist (die Antwort ist offensichtlich “nein”).

    – Superkatze

    29. Mai 2012 um 15:16 Uhr


1646308634 625 Warum sind die Entfernungsmethoden von Java Collections nicht generisch
Colin D

Ich dachte immer, das liegt daran, dass remove() keinen Grund hat, sich darum zu kümmern, welche Art von Objekt Sie ihm geben. Es ist trotzdem einfach genug zu überprüfen, ob dieses Objekt eines der Objekte ist, die die Collection enthält, da es equals() für alles aufrufen kann. Es ist notwendig, den Typ bei add() zu überprüfen, um sicherzustellen, dass er nur Objekte dieses Typs enthält.

  • “Wenn die übergebene Objektreferenz von einem nicht verwandten Typ ist, kann die Sammlung ihn auf keinen Fall enthalten” Falsch. Es muss nur etwas Gleiches enthalten; und Objekte verschiedener Klassen können gleich sein.

    – neues Konto

    29. Mai 2012 um 8:24 Uhr

  • “mag zweifelhaft erscheinen, aber es ist immer noch vollkommen vernünftig” Ist es? Stellen Sie sich eine Welt vor, in der ein Objekt eines Typs nicht immer auf Gleichheit mit einem Objekt eines anderen Typs überprüft werden kann, da parametrisiert wird, welchem ​​Typ es gleich sein kann (ähnlich wie Comparable ist parametrisiert für die Typen, mit denen Sie vergleichen können). Dann wäre es nicht vernünftig, Leuten zu erlauben, etwas Nichtverwandtes zu passieren.

    – neues Konto

    29. Mai 2012 um 8:31 Uhr


  • @newacct: Es gibt einen grundlegenden Unterschied zwischen Größenvergleich und Gleichheitsvergleich: gegebene Objekte A und B eines Typs und X und Y eines anderen, so dass A>Bund X>Y. Entweder A>Y und Y<Aoder X>B und B<X. Diese Beziehungen können nur bestehen, wenn die Größenvergleiche beide Typen kennen. Im Gegensatz dazu kann sich die Gleichheitsvergleichsmethode eines Objekts einfach selbst für ungleich mit irgendetwas anderem Typ erklären, ohne irgendetwas über den betreffenden anderen Typ wissen zu müssen. Ein Objekt vom Typ Cat vielleicht keine Ahnung ob es…

    – Superkatze

    29. Mai 2012 um 15:15 Uhr

  • …”größer als” oder “kleiner als” ein Objekt des Typs FordMustangaber es sollte keine Schwierigkeiten haben zu sagen, ob es einem solchen Objekt gleich ist (die Antwort ist offensichtlich “nein”).

    – Superkatze

    29. Mai 2012 um 15:16 Uhr


1646308635 181 Warum sind die Entfernungsmethoden von Java Collections nicht generisch
Stefan Feuerhahn

Es war ein Kompromiss. Beide Vorgehensweisen haben ihren Vorteil:

  • remove(Object o)
    • ist flexibler. Zum Beispiel ermöglicht es, eine Liste von Zahlen zu durchlaufen und sie aus einer Liste von Longs zu entfernen.
    • Code, der diese Flexibilität nutzt, kann einfacher generiert werden
  • remove(E e) bringt mehr Typsicherheit in das, was die meisten Programme tun möchten, indem sie subtile Fehler zur Kompilierzeit erkennen, wie z.

Abwärtskompatibilität war immer ein Hauptziel bei der Weiterentwicklung der Java-API, daher wurde remove(Object o) gewählt, weil es das Generieren von vorhandenem Code erleichtert. Wenn Abwärtskompatibilität KEIN Problem gewesen wäre, schätze ich, dass die Designer remove(E e) gewählt hätten.

923120cookie-checkWarum sind die Entfernungsmethoden von Java Collections nicht generisch?

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

Privacy policy