java - tutorial - jpanel




Warum entfernen Java Collections keine generischen Methoden? (6)

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

Scheint wie Collection<E> boolean remove(E o);

Wenn Sie dann versehentlich versuchen, zum Beispiel Set<String> anstelle jedes einzelnen Strings aus einer Collection<String> zu entfernen, wäre es später ein Kompilierzeitfehler anstelle eines Debugging-Problems.


Angenommen, Sie haben eine Sammlung von Cat und einige Objektreferenzen der Typen Animal , Cat , SiameseCat und Dog . Es erscheint sinnvoll, die Sammlung zu fragen, ob sie das Objekt enthält, auf das sich die Referenz von Cat oder SiameseCat bezieht. Zu fragen, ob es das Objekt enthält, auf das sich die Animal Referenz bezieht, mag vielleicht zweifelhaft erscheinen, aber es ist immer noch völlig in Ordnung. Das fragliche Objekt könnte schließlich eine Cat und könnte in der Sammlung erscheinen.

Selbst wenn es sich bei dem Objekt um etwas anderes als eine Cat , ist es kein Problem zu sagen, ob es in der Sammlung auftaucht - antworten Sie einfach mit "Nein, tut es nicht". Eine "Lookup-Style" -Sammlung eines Typs sollte in der Lage sein, den Verweis eines beliebigen Supertyps sinnvoll zu akzeptieren und zu bestimmen, ob das Objekt in der Sammlung existiert. Wenn der übergebene Objektreferenz von einem nicht verwandten Typ ist, gibt es keine Möglichkeit, die Sammlung möglicherweise enthalten, so dass die Abfrage in gewissem Sinne nicht sinnvoll ist (es wird immer "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 Objekte, deren Typ nicht mit dem der Sammlung verwandt ist, mit "Nein" zu antworten.


Ein anderer Grund ist wegen der Schnittstellen. Hier ist ein Beispiel, um 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 */
}

Josh Bloch und Bill Pugh beziehen sich in Java Puzzlers IV auf dieses Thema : Die Phantom-Referenz-Bedrohung, Angriff des Klons und Rache der Verschiebung .

Josh Bloch sagt (6:41), dass sie versuchten, die get-Methode von Map zu generalisieren, remove-Methode und einige andere, aber "es hat einfach nicht funktioniert".

Es gibt zu viele sinnvolle Programme, die nicht generierbar sind, wenn Sie nur den generischen Typ der Sammlung als Parametertyp zulassen. Das von ihm gegebene Beispiel ist eine Schnittmenge aus einer List von Number und einer List von Long .


Remove ist keine generische Methode, sodass vorhandener Code, der eine nicht generische Auflistung verwendet, weiterhin kompiliert wird und weiterhin dasselbe Verhalten aufweist.

Weitere Informationen finden Sie unter http://www.ibm.com/developerworks/java/library/j-jtp01255.html .

Bearbeiten: Ein Kommentator fragt, warum die add-Methode generisch ist. [... entfernte meine Erklärung ...] Der zweite Kommentator beantwortete die Frage von Firebird84 viel besser als ich.


Wenn Ihr Typparameter ein Platzhalter ist, können Sie keine generische Remove-Methode verwenden.

Ich erinnere mich, dass ich mit Map's Methode get (Object) auf diese Frage einging. Die get-Methode ist in diesem Fall nicht generisch, obwohl vernünftigerweise erwartet werden sollte, dass ein Objekt des gleichen Typs wie der erste Typparameter übergeben wird. Ich erkannte, dass es keine Möglichkeit gibt, ein Element aus der Map mit dieser Methode zu entfernen, wenn Sie Maps mit einem Platzhalter als ersten Typparameter übergeben, wenn dieses Argument generisch ist. Wildcard-Argumente können nicht wirklich erfüllt werden, da der Compiler nicht garantieren kann, dass der Typ korrekt ist. Ich spekuliere, dass der Grund für das Hinzufügen von generisch ist, dass von Ihnen erwartet wird, dass der Typ korrekt ist, bevor Sie ihn der Sammlung hinzufügen. Wenn ein Objekt jedoch entfernt wird, wenn 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, das GARANTIEREN zu dieser Sammlung gehört, weil Sie in der vorherigen Zeile einen Verweis darauf erhalten haben ....

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


Zusätzlich zu den anderen Antworten gibt es einen weiteren Grund, warum die Methode ein Object akzeptieren sollte, das 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, das an die remove Methode übergeben wird, für die Definition der equals Methode verantwortlich ist. Gebäudeprädikate werden auf diese Weise sehr einfach.





collections