Это должно поддержать назад совместимость с более старыми (un-genericized) версиями Collection
? Или есть ли более тонкая деталь, которую я пропускаю? Я вижу этот шаблон, повторенный в remove
также (remove(Object o)
), но add
genericized как add(E e)
.
contains ()
принимает объект
, потому что объект, которому он соответствует, не обязательно должен быть того же типа, что и объект, который вы передаете в , содержит ()
; требуется только, чтобы они были равны. Из спецификации contains ()
, contains (o)
возвращает истину, если существует объект e
такой, что (o == null? E == null: o.equals (e))
верно.Обратите внимание, что нет ничего, требующего, чтобы o
и e
были одного типа. Это следует из того факта, что метод equals ()
принимает в качестве параметра Object
, а не только того же типа, что и объект.
Хотя обычно может быть правдой, что для многих классов equals ()
определено так, что его объекты могут быть равны только объектам его собственного класса, это, конечно, не всегда так. Например, спецификация для List.equals ()
говорит, что два объекта List
равны, если они оба являются List
s и имеют одинаковое содержимое, даже если это разные реализации List. Итак, возвращаясь к примеру в этом вопросе, можно иметь Collection
, и я могу вызвать contains ()
с LinkedList
как аргумент, и он может вернуть истину, если есть список с таким же содержимым. Это было бы невозможно, если бы contains ()
был универсальным и ограничивал его тип аргумента до E
.
Фактически, тот факт, что contains ()
принимает любой объект в качестве аргумента, дает интересное использование, когда вы можете использовать его для проверки существования объекта в коллекции, который удовлетворяет определенному свойству. :
Collection<Integer> integers;
boolean oddNumberExists = integers.contains(new Object() {
public boolean equals(Object e) {
Integer i = (Integer)e;
if (i % 2 != 0) return true;
else return false;
}
});
Ответили здесь.
Почему методы Java Collections remove не являются generic?
Вкратце, они хотели максимизировать обратную совместимость, потому что коллекции были введены задолго до generics.
И добавлю от себя: видео, на которое он ссылается, стоит посмотреть.
http://www.youtube.com/watch?v=wDN_EYUvUq0
update
Чтобы уточнить, человек, который это сказал (в видео), был одним из тех, кто обновил java maps и collections для использования generics. Если он не знает, то кто.
Потому что иначе он мог бы сравниваться только с точным совпадением типа параметра, специально подстановочные коллекции перестали бы работать, например.
class Base
{
}
class Derived
extends Base
{
}
Collection< ? extends Base > c = ...;
Derived d = ...;
Base base_ref = d;
c.contains( d ); // Would have produced compile error
c.contains( base_ref ); // Would have produced compile error
EDIT
Для сомневающихся, которые думают, что это не одна из причин, вот модифицированный список массивов с генерированным методом contains
class MyCollection< E > extends ArrayList< E >
{
public boolean myContains( E e )
{
return false;
}
}
MyCollecttion< ? extends Base > c2 = ...;
c2.myContains( d ); // does not compile
c2.myContains( base_ref ); // does not compile
По сути contains( Object o )
- это хак, чтобы заставить этот очень распространенный случай использования работать с Java Generics.
"содержит ли эта корзина яблок этот апельсин?"
очевидно, что ответ TRUE не может быть дан. но это все еще оставляет слишком много возможностей:
collection api выбрал первый вариант. но и второй вариант был бы вполне логичен. такой вопрос в 99.99% случаев - полная чушь, так что даже не задавайте его!
Это потому, что функция contains
использует функцию equals
, а функция equals
определена в базовом классе Object с сигнатурой equals(Object o)
, а не equals(E e)
(поскольку не все классы являются общими). Тот же случай с функцией remove
- она обходит коллекцию, используя функцию equals
, которая принимает аргумент Object.
Однако это не объясняет решение напрямую, поскольку они могли бы по-прежнему использовать тип E и позволить ему автоматически приводиться к типу Object при вызове equals
; но я полагаю, что они хотели позволить функции вызываться на других типах Object. Нет ничего плохого в том, чтобы иметь Collection
и затем вызвать c.contains(somethingOfTypeBar)
- это всегда будет возвращать false, и таким образом устраняется необходимость приведения к типу Foo (которое может вызвать исключение) или, для защиты от исключения, вызова typeof
. Поэтому вы можете представить, что если вы итерируете что-то со смешанными типами и вызываете contains
для каждого из элементов, вы можете просто использовать функцию contains для всех них, а не использовать защитные функции.
Это действительно напоминает "новые" свободно типизированные языки, если посмотреть на это с другой стороны...