Общий сбор
Это часть интерфейса коллекции Java (1.6):
public interface Collection<E> extends java.lang.Iterable<E> {
/* ... */
boolean containsAll(java.util.Collection<?> objects);
boolean addAll(java.util.Collection<? extends E> es);
boolean removeAll(java.util.Collection<?> objects);
boolean retainAll(java.util.Collection<?> objects);
/* ... */
}
Почему addAll
имеет <? extends E>
, а removeAll
имеет <?>
Ответы
Ответ 1
Я не знал, что я googled. Я получил это объяснение здесь: http://www.ibm.com/developerworks/java/library/j-jtp01255/index.html
Копирование части:
Один элемент обобщенного API коллекций, который вначале часто путается, - это подписи containsAll(), removeAll() и retainAll(). Вы можете ожидать, что подписи для remove() и removeAll() будут:
interface Collection<E> {
public boolean remove(E e); // not really
public void removeAll(Collection<? extends E> c); // not really
}
Но это на самом деле:
interface Collection<E> {
public boolean remove(Object o);
public void removeAll(Collection<?> c);
}
Почему это? Опять же, ответ кроется в обратной совместимости. Контракт интерфейса x.remove(o) означает "если o содержится в x, удалите его, иначе ничего не делайте". Если x - общий набор, o не обязательно должен быть совместим с типом параметра типа x. Если removeAll() был генерализован только для вызываемого, если его аргумент был совместим с типом (Collection<? extends E>
), то определенные последовательности кода, которые были законными до дженериков, стали бы незаконными, например:
// a collection of Integers
Collection c = new HashSet();
// a collection of Objects
Collection r = new HashSet();
c.removeAll(r);
Если вышеприведенный фрагмент был генерализован очевидным образом (сделав ca Collection<Integer>
и ra Collection<Object>
), тогда код выше не будет компилироваться, если подпись removeAll() потребовала, чтобы его аргумент был Collection<? extends E>
, вместо того, чтобы быть не-op. Одной из ключевых целей генерации библиотек классов было не разбить или изменить семантику существующего кода, поэтому remove(), removeAll(), retainAll() и containsAll() должны были быть определены с более слабым типом ограничений, чем они возможно, они были переработаны с нуля для дженериков.
Ответ 2
Для любой коллекции, содержащей элементы типа E
, addAll
должен иметь возможность обрабатывать входные коллекции не только E
, но и все ее подклассы. Следовательно, <? extends E>
. Без этого вы не могли бы добавить все элементы List<Integer>
в List<Number>
, что явно не правильно. *
Для удаления ограничения не должны быть строго установлены, и нет никакого вреда в попытке удалить элементы коллекции какого-то совершенно несвязанного типа. Например. вы можете иметь коллекцию Number
s, о которой вам известно, что она содержит только Integer
s, поэтому передача ее в removeAll
на List<Integer>
должна работать нормально, и было бы глупо для компилятора чтобы запретить это.
Обратите внимание, что в соответствии с Javadoc, removeAll
может опционально генерировать ClassCastException
, в зависимости от реализации.
* Причина в том, что в Java дженерики являются инвариантными. Более подробно см., Например, этот поток.
Ответ 3
<?>
менее ограничительный, чем <? extends E>
.
Нет ничего плохого в удалении апельсина из коллекции яблок; есть много чего не так с добавлением апельсина в коллекцию яблок.
Ответ 4
Когда вы добавляете элемент в свою коллекцию, вы должны быть уверены, что у них есть определенный тип.
Когда вы удаляете их, удаляются только те из коллекции. Независимо от их типа.
Ответ 5
Java реализует дженерики через стирание. Эти данные предназначены только для времени компиляции. Я предполагаю, что разработчики коллекции java сделали это, чтобы сохранить более высокую совместимость с версией java-версии до генериков.
Ответ 6
когда вы добавляете объект, он должен быть подклассом (или под-подклассом и т.д.) основного типа. Когда вы удаляете объект, он возвращает его как тип коллекции. Это хороший пример polymorphism в действии.
Ответ 7
Кто заботится о том, что вы пытаетесь удалить?
Добавление - это что-то еще; мы не хотели бы иметь что-то странное в нашей коллекции.
по запросу; пример:
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class Main {
private static class A {
}
public static void main(String[] args) {
Collection<A> collection_A = new ArrayList<A>();
Collection<String> collection = new ArrayList<String>();
// no problem to try and remove things that wouldn't be there in the first place; either way, they are gone afterwards
collection.removeAll(collection_A);
// we can't allow this; you would end up with things in your collection that don't belong there
collection.addAll(collection_A);
}
}
Ответ 8
Простой пример, иллюстрирующий сказанное:
public class Test {
public static void main(String[] args) {
List<String> l = new ArrayList<String>();
System.out.println(l.remove(new Object())); //false
System.out.println(l.contains(new Object())); //false
// l.add(new Object()); // does not compile
}
}
Ответ 9
Чтобы удалить ограничение, не нужно, поэтому только <?>
, но при добавлении мы должны проверить и затем добавить для безопасности типа, поэтому addAll имеет ограничение <? extends E>
Ответ 10
С помощью addAll вы хотите добавить все элементы, являющиеся подтипом родового типа. Сюда входит добавление всех элементов List<String>
к List<Object>
. Мы используем ? extends E
для принятия любой коллекции, содержащей тип, хранящийся в этой коллекции или любой подтип.
boolean addAll(java.util.Collection<? extends E> es);
List<Number> numbers = ...;
List<Integer> integers = ...;
numbers.addAll(integers);//works
boolean addAll(java.util.Collection<E> es);
numbers.addAll(integers);//does not work E != Integer
мы не можем использовать ?
, поскольку это приведет к удалению любой безопасности, предоставляемой генериками.
boolean addAll(java.util.Collection<? extends E> es);
List<Number> numbers = ...;
List<Integer> integers = ...;
List<String> strings = ...;
numbers.addAll(integers);//works
numbers.addAll(strings);//error
boolean addAll(java.util.Collection<?> es);
numbers.addAll(strings);//works - now we have strings in our Number collection
Мы можем использовать ?
для удаления объектов, поскольку попытка удалить строку из списка номеров не влияет на List<Number>
.
boolean removeAll(java.util.Collection<?> objects);
List<Objects> objects = ...;
List<Integer> integers = ...;
List<Number> numbers = ...;
numbers.removeAll(objects);//works
numbers.removeAll(integers);//works
boolean removeAll(java.util.Collection<? extends E> objects);
numbers.removeAll(objects);//does not work
numbers.removeAll(integers);//works
boolean removeAll(java.util.Collection<? super E> objects);
numbers.removeAll(objects);//works
numbers.removeAll(integers);//does not work