Dlaczego Kolekcje Javy nie usuwają metod generycznych?

Dlaczego nie jest Kolekcja.remove(Object o) generic?

Wydaje się, że Collection<E> może mieć boolean remove(E o);

Następnie, gdy przypadkowo spróbujesz usunąć (na przykład) Set<String> zamiast każdego pojedynczego ciągu znaków z Collection<String>, będzie to błąd czasu kompilacji zamiast problemu z debugowaniem później.

Author: Chris Mazzola, 2008-09-19

10 answers

Josh Bloch i Bill Pugh odnoszą się do tej kwestii w Java Puzzlers IV: The Phantom Reference Menace, Attack of the Clone, and Revenge of The Shift.

Josh Bloch mówi (6:41) , że próbowali uogólnić metodę get Map, Usuń metodę i kilka innych, ale "to po prostu nie działa".

Istnieje zbyt wiele rozsądnych programów, których nie da się uogólnić, jeśli jako typ parametru zezwalasz tylko na typ ogólny kolekcji. Przykład podany przez niego jest przecięciem List Number s I a List z Long s.

 73
Author: dmeister,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-04 11:38:40

remove() (in Map Jak Również in Collection) nie jest generyczny, ponieważ powinieneś być w stanie przekazać dowolny typ obiektu do remove(). Usuwany obiekt nie musi być tego samego typu co obiekt, który przekazujesz remove(); wymaga tylko, aby były równe. Ze specyfikacji remove(), remove(o) usuwa obiekt e taki, że (o==null ? e==null : o.equals(e)) jest true. Zauważ, że nie ma nic wymagającego, aby o i e były tego samego typu. Wynika to z faktu, że metoda equals() przyjmuje Object jako parametr, a nie tylko ten sam typ co obiekt.

Chociaż może być powszechnie prawdą, że wiele klas ma equals() zdefiniowane tak, że jej obiekty mogą być równe tylko obiektom własnej klasy, to z pewnością nie zawsze tak jest. Na przykład Specyfikacja List.equals() mówi, że dwa obiekty List są równe, jeśli są obiema listami i mają tę samą zawartość, nawet jeśli są różnymi implementacjami List. Wracając więc do przykładu w tym pytaniu, można mieć {[17] } i dla mnie wywołanie {[0] } z LinkedList jako argumentem, i powinno usunąć klucz, który jest listą o tej samej zawartości. Nie byłoby to możliwe, gdyby remove() były ogólne i ograniczały swój typ argumentu.

 75
Author: newacct,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-09 13:22:16

Ponieważ jeśli parametr type jest wieloznaczny, nie można użyć ogólnej metody usuwania.

Przypominam sobie, że wpadłem na to pytanie za pomocą metody get(Object) Map. Metoda get w tym przypadku nie jest generyczna, chociaż należy oczekiwać, że zostanie przekazana obiektowi tego samego typu, co pierwszy parametr typu. Zdałem sobie sprawę, że jeśli przekazujesz mapy z symbolem wieloznacznym jako pierwszym parametrem typu, to nie ma sposobu, aby uzyskać element z mapy za pomocą tej metody, jeśli argument był ogólny. Argumenty wieloznaczne nie mogą być spełnione, ponieważ kompilator nie może zagwarantować, że typ jest prawidłowy. Spekuluję, że powodem, dla którego add jest ogólny, jest to, że oczekuje się, że zagwarantujesz, że typ jest poprawny przed dodaniem go do kolekcji. Jednak podczas usuwania obiektu, Jeśli typ jest nieprawidłowy, to i tak nie będzie pasował do niczego. Jeśli argument byłby znakiem wieloznacznym, metoda byłaby po prostu bezużyteczna, nawet jeśli możesz mieć obiekt, który możesz Gwarancja należy do tej kolekcji, ponieważ właśnie dostałeś odniesienie do niej w poprzedniej linii....

Prawdopodobnie nie wyjaśniłem tego zbyt dobrze, ale wydaje mi się to wystarczająco logiczne.

 11
Author: Bob Gettys,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-19 19:41:37

Oprócz innych odpowiedzi, istnieje jeszcze jeden powód, dla którego metoda powinna przyjmować Object, czyli predykaty. Rozważmy następującą próbkę:

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);
        }});
    }
}

Chodzi o to, że obiekt przekazywany do metody remove jest odpowiedzialny za zdefiniowanie metody equals. Budowanie predykatów staje się w ten sposób bardzo proste.

 6
Author: Hosam Aly,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-01 16:41:29

Załóżmy, że mamy zbiór Cat, a niektóre odniesienia do obiektów typów Animal, Cat, SiameseCat, i Dog. Pytanie, czy Kolekcja zawiera obiekt, do którego odnosi się odniesienie Cat lub SiameseCat wydaje się rozsądne. Pytanie, czy zawiera obiekt, do którego odnosi się odniesienie Animal, może wydawać się podejrzane, ale nadal jest całkowicie rozsądne. Przedmiot, o którym mowa, w końcu może być Cat i może pojawić się w kolekcji.

Dalej, nawet jeśli obiekt stanie się być czymś innym niż Cat, nie ma problemu z tym, czy pojawia się w zbiorze-po prostu odpowiedz "Nie, Nie ma". Zbiór typu "lookup-style" jakiegoś typu powinien być w stanie w znaczący sposób zaakceptować odniesienie do dowolnego supertype i określić, czy obiekt istnieje w kolekcji. Jeśli przekazywany obiekt jest niepowiązanego typu, nie ma możliwości, aby kolekcja mogła go zawierać, więc zapytanie w pewnym sensie nie ma sensu (zawsze odpowie "nie"). Niemniej jednak, ponieważ nie ma sposobu na ograniczenie parametrów do podtypów lub supertypów, najbardziej praktycznym jest zaakceptowanie dowolnego typu i odpowiedź "nie" dla obiektów, których Typ nie jest związany z typem kolekcji.

 5
Author: supercat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-05-28 20:49:08

Zawsze myślałem, że to dlatego, że remove() nie ma powodu, by przejmować się, jaki typ obiektu mu podajesz. Bez względu na to, łatwo jest sprawdzić, czy ten obiekt jest jednym z tych, które zawiera Kolekcja, ponieważ może wywoływać equals() na czymkolwiek. Należy sprawdzić typ w add (), aby upewnić się, że zawiera tylko obiekty tego typu.

 4
Author: ColinD,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-19 20:40:40

To był kompromis. Oba podejścia mają swoją zaletę:

  • remove(Object o)
    • jest bardziej elastyczny. Na przykład pozwala na iterację poprzez listę liczb i usunięcie ich z listy długów.
    • kod wykorzystujący tę elastyczność można łatwiej uogólnić
  • remove(E e) zwiększa bezpieczeństwo typów do tego, co większość programów chce zrobić, wykrywając subtelne błędy w czasie kompilacji, na przykład omyłkowo próbując usunąć liczbę całkowitą z listy szorty.

Zgodność wsteczna była zawsze głównym celem przy rozwijaniu API Javy, dlatego wybrano opcję remove (Object o), ponieważ ułatwiło to uogólnienie istniejącego kodu. Gdyby wsteczna kompatybilność nie była problemem, zgaduję, że projektanci zdecydowaliby się usunąć (E e).

 0
Author: Stefan Feuerhahn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-07-24 08:21:36

Remove nie jest metodą generyczną, więc istniejący kod wykorzystujący inną niż generyczna kolekcję będzie nadal kompilowany i będzie zachowywał się tak samo.

Zobacz http://www.ibm.com/developerworks/java/library/j-jtp01255.html Po szczegóły.

Edit: komentator pyta, dlaczego metoda add jest ogólna. [...usunąłem moje wyjaśnienie...] Drugi komentator odpowiedział na pytanie od firebird84 dużo lepiej niż ja.

 -1
Author: Jeff C,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-19 20:14:02

Innym powodem są interfejsy. Oto przykład, aby to pokazać:

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 */
}
 -2
Author: Patrick,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-26 13:25:12

Ponieważ złamałby istniejący (pre-Java5) kod. np.,

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

Teraz Można powiedzieć, że powyższy kod jest błędny, ale załóżmy, że o pochodzi z heterogenicznego zbioru obiektów (tzn. zawierał ciągi znaków, liczbę, obiekty itp.). Chcesz usunąć wszystkie dopasowania, co było legalne, ponieważ Usuń po prostu ignoruje non-ciągów, ponieważ nie były równe. Ale jeśli go usunąć (ciąg o), że nie działa.

 -3
Author: noah,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-09-19 19:47:35