Frage

Warum ist nicht Collection.remove (Object o) Generika?

Scheint, wie Collection<E> könnte boolean remove(E o); haben

Dann, wenn Sie versehentlich versuchen (zum Beispiel) Set<String> zu entfernen, anstatt jedes einzelnen String von einem Collection<String>, wäre es später statt einem Debug-Problem eine Kompilierung Fehler sein.

War es hilfreich?

Lösung

Josh Bloch und Bill Pugh zu diesem Thema finden Sie in Java Puzzlers IV: Phantom Menace Reference, Angriff des Klon und Rache des Umschalttaste .

Josh Bloch sagt (6.41), dass sie die Methode get zu generify versucht von Karte entfernen Methode und einige andere, aber „es hat einfach nicht funktioniert“.

Es gibt zu viele vernünftige Programme, die nicht generified werden könnten, wenn Sie erlauben nur die allgemeine Art der Sammlung als Parametertyp. Das Beispiel von ihm gegeben ist ein Schnittpunkt einer List von Numbers und einem List von Longs.

Andere Tipps

remove() (in Map sowie in Collection) ist nicht generisch, weil Sie in der Lage sein sollten, in jeder Art von Objekt zu remove() passieren. Das Objekt entfernt muss nicht der gleiche Typ wie das Objekt, das Sie sich in remove() passieren; es erfordert nur, dass sie gleich sein. Aus der Beschreibung des remove() entfernt remove(o) das Objekt e so dass (o==null ? e==null : o.equals(e)) true ist. Beachten Sie, dass es nichts erfordert o und e die gleiche Art zu sein. Dies folgt aus der Tatsache, dass die equals() Methode in einem Object als Parameter übernimmt, nicht nur den gleichen Typ wie das Objekt.

Obwohl es allgemein wahr sein, dass viele Klassen equals() definiert haben, so dass ihre Objekte nur gleich Objekte seiner eigenen Klasse sein können, das ist sicherlich nicht immer der Fall. Zum Beispiel sagt die Spezifikation für List.equals() dass zwei Listen Objekte gleich sind, wenn sie beide Listen sind und haben den gleichen Inhalt, auch wenn sie unterschiedliche Implementierungen von List sind. So zum Beispiel zurückzukommen in dieser Frage, ist es möglich, ein Map<ArrayList, Something> zu haben und für mich remove() mit einem LinkedList als Argument zu nennen, und es soll den Schlüssel entfernen, die eine Liste mit dem gleichen Inhalt ist. Dies wäre nicht möglich, wenn remove() generic ist und beschränkt sein Argument Typen.

Weil, wenn Ihr Typ-Parameter ein Platzhalter ist, können Sie nicht eine allgemeine entfernen Methode verwenden.

Ich scheine in dieser Frage zu erinnern, läuft mit Karte des get (Object) -Methode. Die get-Methode in diesem Fall nicht generisch, wenn es vernünftig, ein Objekt des gleichen Typs wie die ersten Typ-Parameter übergeben werden soll erwarten. Ich erkennen, dass, wenn Sie vorbei um Karten mit einem Platzhalter als ersten Typ-Parameter, dann gibt es keine Möglichkeit, ein Element aus der Karte mit dieser Methode zu bekommen, wenn das Argument generic ist. Wildcard Argumente können nicht wirklich zufrieden sein, weil der Compiler kann nicht garantieren, dass der Typ korrekt ist. Ich spekuliere, dass der Grund add ist generisch ist, dass Sie garantiert sind zu erwarten, dass der Typ richtig ist es in die Sammlung vor der Zugabe. Wenn jedoch ein Objekt zu entfernen, wenn der Typ nicht korrekt ist, dann wird es nichts sowieso passen. Ist das Argument eine Wildcard das Verfahren waren einfach unbrauchbar wäre, auch wenn Sie ein Objekt haben kann zu dieser Sammlung, die Sie garantieren können, gehört, weil Sie nur einen Verweis auf sie in der vorherigen Zeile bekam ....

ich wahrscheinlich habe es nicht sehr gut erklären, aber es scheint logisch genug für mich.

Zusätzlich zu den anderen Antworten, es ist ein weiterer Grund, warum die Methode ein Object annehmen sollte, die 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 übergeben wird, um die remove Verfahren zur Definition des equals Verfahren verantwortlich ist. Gebäude Prädikate werden sehr einfach auf diese Weise.

Angenommen man eine Sammlung von Cat hat, und einige Objektreferenzen von Typen Animal, Cat, SiameseCat und Dog. die Sammlung zu fragen, ob es das Objekt enthält, durch die Cat oder SiameseCat genannte Referenz scheint vernünftig. Zu fragen, ob es das Objekt enthält, durch die Animal genannte Referenz kann vertrackt sein, aber es ist immer noch vollkommen in Ordnung. Das betreffende Objekt könnte immerhin sein ein Cat und könnte in der Sammlung erscheinen.

Ferner kann, selbst wenn das Objekt etwas anderes als ein Cat passiert sein, dann ist es kein Problem zu sagen, ob es in der Sammlung erscheint - einfach beantworten: „Nein, es funktioniert nicht“. Eine „Lookup-style“ Sammlung von irgendeiner Art sollte in der Lage sein, sinnvoll Bezug aller übergeordneten Typs zu akzeptieren und bestimmen, ob das Objekt in der Sammlung vorhanden ist. Wenn die übergebene Objekt anhand eines nicht verwandten Typs ist, gibt es keine Möglichkeit, vielleicht die Sammlung es enthalten könnte, so dass die Abfrage in einem gewissen Sinne ist nicht sinnvoll (es wird immer „nein“ beantworten). Dennoch, da keine Möglichkeit besteht Parameter zu beschränken, um Subtypen oder übergeordneten Typen zu sein, ist es am praktischsten, einfach jede Art zu akzeptieren und Antwort „nein“ für alle Objekte, deren Typ in keinem Zusammenhang mit der von der Sammlung.

Ich dachte immer war dies, weil remove () hat keinen Grund zu kümmern, welche Art von Objekt, das Sie ihr geben. Es ist leicht genug, egal, um zu überprüfen, ob das Objekt einer von denen ist die Sammlung enthält, da es equals () auf etwas aufrufen kann. Es ist notwendig, Typen auf Add zu überprüfen (), um sicherzustellen, dass es nur Objekte dieses Typs enthält.

Entfernen ist nicht eine generische Methode, so dass bestehende Code, um eine nicht-generische Sammlung mit noch kompilieren und noch das gleiche Verhalten haben.

Siehe http://www.ibm.com/developerworks/ java / library / j-jtp01255.html .

Edit: Ein Kommentator fragt, warum die Add-Methode generisch ist. [... entfernt meine Erklärung ...] Zweite Kommentator beantwortet die Frage von firebird84 viel besser als ich.

Ein weiterer Grund ist, weil der Schnittstellen. Hier ist ein Beispiel, es zu zeigen:

public interface A {}

public interface B {}

public class MyClass implements A, B {}

public static void main(String[] args) {
   Collection<A> collection = new ArrayList<>();
   MyClass item = new MyClass();
   collection.add(item);  // works fine
   B b = item; // valid
   collection.remove(b); /* It works because the remove method accepts an Object. If it was generic, this would not work */
}

Weil es würde brechen bestehende (pre-Java5) Code. z. B.

Set stringSet = new HashSet();
// do some stuff...
Object o = "foobar";
stringSet.remove(o);

Nun könnte man sagen, dass der obige Code falsch ist, aber angenommen, dass o aus einer heterogenen Gruppe von Objekten kam (das heißt, es Strings enthalten sind, Anzahl, Objekte, etc.). Sie wollen alle Spiele entfernen, was legal war, weil entfernen würde nur die nicht-Strings ignorieren, weil sie nicht gleich waren. Aber wenn Sie es entfernen lassen (String o), das nicht mehr funktioniert.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top