Почему у нас есть (Object o), а не содержит (E e)?

Следует ли поддерживать обратную совместимость со старыми (не обобщенными) версиями Collection? Или есть более тонкие детали, которые мне не хватает? Я также вижу этот шаблон в remove (remove(Object o)), но add генерируется как add(E e).

Ответы

Ответ 1

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

Хотя обычно бывает так, что многие классы имеют equals(), поэтому его объекты могут быть равны только объектам своего класса, что, конечно, не всегда так. Например, спецификация для List.equals() говорит о том, что два объекта List равны, если они оба являются List и имеют одинаковое содержимое, даже если они представляют собой разные реализации List. Поэтому, возвращаясь к примеру в этом вопросе, возможно иметь Collection<ArrayList> и для меня вызвать contains() с аргументом LinkedList как, и он может вернуть true, если есть список с тем же содержимым. Это было бы невозможно, если 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;
    }
});

Ответ 2

Отвечено здесь. Почему Java Collection не удаляет общие методы? Короче говоря, они хотели максимизировать обратную совместимость, потому что коллекции были введены задолго до дженериков.

И добавить от меня: видео, на которое он ссылается, стоит посмотреть.
http://www.youtube.com/watch?v=wDN_EYUvUq0

Обновление
Чтобы уточнить, человек, который сказал, что (в видео) был одним из тех, кто обновлял java-карты и коллекции для использования дженериков. Если он не знает, то кто.

Ответ 3

Это связано с тем, что функция contains использует функцию equals, а функция equals определена в базовом классе Object с сигнатурой equals(Object o), а не equals(E e) (так как не все классы являются общими). Тот же случай с функцией remove - он обходит коллекцию с помощью функции equals, которая принимает аргумент Object.

Это не объясняет, однако, прямое решение, поскольку они все еще могли использовать тип E и позволяли ему автоматически присваивать тип Object при вызове equals; но я думаю, они хотели, чтобы функция вызывалась на другие типы объектов. Нет ничего плохого в том, что у меня есть Collection<Foo> c;, а затем вызывается c.contains(somethingOfTypeBar) - он всегда будет возвращать false, и поэтому он исключает необходимость приведения типа Foo (который может генерировать исключение) или, чтобы защитить от исключения, typeof вызов. Таким образом, вы можете себе представить, что если вы повторяете что-то со смешанными типами и вызываете contains для каждого из элементов, вы можете просто использовать функцию contains для всех из них, а не для защиты.

Это на самом деле напоминает "более новые" языки с плохой типизацией, когда вы смотрите на него таким образом...

Ответ 4

Так как в противном случае его можно было бы сравнить только с точным соответствием типа параметра, то, в частности, подстановочные коллекции перестали работать, например.

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

ИЗМЕНИТЬ
Для сомневающихся, которые думают, что не одна из причин, вот список измененных массивов с a будет генерироваться, содержит метод

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.

Ответ 5

"есть ли в этой корзине яблоки этот апельсин?"

ясно, что TRUE-ответ не может быть задан. но это все еще оставляет слишком много возможностей:

  • ответ ЛОЖЬ.
  • вопрос не совсем сформирован, он не должен передавать компиляцию.

коллекция api выбрала первый. но второй выбор также имеет смысл. вопрос вроде этого - вопрос о дерьме 99,99% раз, поэтому даже не спрашивайте!