Вопрос-ответ

Why aren't Java Collections remove methods generic? [duplicate]

Почему методы удаления коллекций Java не являются универсальными?

Почему Collection.remove(Object o) не является универсальным?

Похоже, что Collection<E> могли бы boolean remove(E o);

Тогда, когда вы случайно попытаетесь удалить (например) Set<String> вместо каждой отдельной строки из a Collection<String>, это будет ошибка времени компиляции, а не проблема последующей отладки.

Переведено автоматически
Ответ 1

Джош Блох и Билл Пью ссылаются на эту проблему в Java Puzzlers IV: The Phantom Reference Menace, Attack of the Clone и Revenge of The Shift.

Джош Блох говорит (6: 41), что они пытались обобщить метод get для Map, метод remove и некоторые другие, но "это просто не сработало".

Существует слишком много разумных программ, которые нельзя было бы обобщить, если бы вы разрешили только универсальный тип коллекции в качестве типа параметра. Приведенный им пример представляет собой пересечение a List of Numbers и a List of Longs.

Ответ 2

remove()Map а также в Collection) не является универсальным, потому что вы должны иметь возможность передавать объекты любого типа в remove(). Удаленный объект не обязательно должен быть того же типа, что и объект, которому вы передаете remove(); требуется только, чтобы они были равны. Из спецификации remove(), remove(o) удаляет объект e таким образом, что (o==null ? e==null : o.equals(e)) является true. Обратите внимание, что нет ничего, что требовало бы, чтобы o и e были одного и того же типа. Это следует из того факта, что equals() метод принимает параметр Object as, а не просто тот же тип, что и у объекта.

Хотя обычно может быть правдой, что многие классы equals() определены так, что их объекты могут быть равны только объектам их собственного класса, это, конечно, не всегда так. Например, в спецификации для List.equals() говорится, что два объекта List равны, если они оба являются списками и имеют одинаковое содержимое, даже если они являются разными реализациями List. Итак, возвращаясь к примеру в этом вопросе, можно иметь Map<ArrayList, Something> и для меня вызывать remove() с LinkedList в качестве аргумента, и это должно удалить ключ, который представляет собой список с тем же содержимым. Это было бы невозможно, если бы remove() были универсальными и ограничивали тип аргумента.

Ответ 3

Потому что, если ваш параметр типа является шаблоном, вы не можете использовать универсальный метод удаления.

Кажется, я припоминаю, что сталкивался с этим вопросом с помощью метода get (Object) Map. Метод get в данном случае не является универсальным, хотя разумно ожидать, что ему будет передан объект того же типа, что и первый параметр типа. Я понял, что если вы передаете карты с подстановочным знаком в качестве первого параметра типа, то нет способа извлечь элемент из карты с помощью этого метода, если этот аргумент был универсальным. Подстановочные аргументы на самом деле не могут быть удовлетворены, потому что компилятор не может гарантировать правильность типа. Я предполагаю, что причина, по которой add является универсальным, заключается в том, что вы должны гарантировать правильность типа перед добавлением его в коллекцию. Однако при удалении объекта, если тип неверен, он все равно ничему не будет соответствовать. Если бы аргумент был шаблоном, метод был бы просто непригоден для использования, даже если у вас может быть объект, принадлежность которого вы можете ГАРАНТИРОВАТЬ к этой коллекции, потому что вы только что получили ссылку на него в предыдущей строке....

Возможно, я не очень хорошо это объяснил, но мне это кажется достаточно логичным.

Ответ 4

Предположим, что у вас есть коллекция Cat и некоторые ссылки на объекты типов Animal, Cat, SiameseCat и Dog. Спрашивать коллекцию, содержит ли она объект, на который ссылается ссылка Cat или SiameseCat, кажется разумным. Спрашивать, содержит ли он объект, на который ссылается Animal ссылка, может показаться изворотливым, но это все равно вполне разумно. В конце концов, рассматриваемый объект может быть Cat и может появиться в коллекции.

Кроме того, даже если объект является чем-то отличным от Cat, нет проблем с определением, отображается ли он в коллекции - просто ответьте "нет, это не так". Коллекция в стиле "поиска" некоторого типа должна быть способна осмысленно принимать ссылку на любой супертип и определять, существует ли объект в коллекции. Если передаваемая ссылка на объект имеет несвязанный тип, коллекция никак не может содержать ее, поэтому запрос в некотором смысле не имеет смысла (он всегда ответит "нет"). Тем не менее, поскольку нет никакого способа ограничить параметры подтипами или супертипами, наиболее практично просто принять любой тип и ответить "нет" для любых объектов, тип которых не связан с типом коллекции.

java generics collections