Может ли кто-нибудь объяснить объявление этих общих методов Java?

Я читаю "Generics in the Java Programming Language" Гилада Брачи и не понимаю стиль объявления. Следующий код находится на странице 8:

interface Collection<E> 
{ 
    public boolean containsAll(Collection<?> c); 
    public boolean addAll(Collection<? extends E> c); 
} 


interface Collection<E> 
{ 
    public <T> boolean containsAll(Collection<T> c);
    public <T extends E> boolean addAll(Collection<T> c); 
    // hey, type variables can have bounds too! 
} 

Меня смущает второе заявление. Мне непонятно, для чего служит объявление <T> в следующей строке:

    public <T> boolean containsAll(Collection<T> c);

Метод уже имеет связанный с ним тип (логический).

Зачем вам использовать <T> и что он сообщает компилятору?

Я думаю, что мой вопрос должен быть немного более конкретным.

Почему вы пишете:

  public <T> boolean containsAll(Collection<T> c);

vs

  public boolean containsAll(Collection<T> c);

Мне непонятно, какова цель <T> в первом объявлении containsAll.


person Tony Giaccone    schedule 14.01.2011    source источник
comment
Если вы понимаете PECS, вы поймете и это. Проверьте здесь stackoverflow.com/questions/4535930/   -  person Aravind Yarram    schedule 14.01.2011
comment
В Oracle/Sun JDK 6 используется первый пример, поэтому я не знаю, будете ли вы использовать и второй.   -  person Peter Lawrey    schedule 14.01.2011
comment
И чем он лучше public boolean containsAll(Collection<?> c);?   -  person Mark Peters    schedule 14.01.2011


Ответы (6)


Насколько я могу судить, в данном случае <T> вообще ничего полезного не дает. Он создает метод, который полностью функционально эквивалентен методам, использующим подстановочные знаки.

Вот несколько примеров, когда это было бы полезно:

public List<?> transform(List<?> in);
//vs
public <T> List<T> transform(List<T> in);

В приведенном выше вы можете сопоставить тип возвращаемого значения с типом ввода. Первый пример не может сопоставить тип времени выполнения двух подстановочных знаков.

public void add(List<?> list, Object obj);
//vs
public <T> void add(List<? super T> list, T obj);

В приведенном выше примере первый метод даже не сможет добавить obj к list, поскольку его нельзя считать типобезопасным. Общий параметр во втором гарантирует, что list может содержать любой тип obj.

person Mark Peters    schedule 14.01.2011

Метод уже имеет связанный с ним тип (логический).

Это тип return. Полный тип метода — «метод, который принимает параметр Collection<T> (для некоторого T) и возвращает boolean».

И вот тут-то и появляется T: его использует параметр функции. Другими словами, этот метод можно вызывать с разными типами в качестве аргумента. Единственное ограничение этих типов заключается в том, что они должны реализовывать интерфейс Collection<T>, который сам опирается на общий аргумент T (тип объектов, хранящихся в коллекции).

person Konrad Rudolph    schedule 14.01.2011
comment
Но чем это лучше, чем, казалось бы, функциональный эквивалент public boolean containsAll(Collection<?> c)? Аналогично, почему не public boolean addAll(Collection<? extends E> c)? - person Mark Peters; 14.01.2011
comment
Это лучше, потому что тело или определение метода может ссылаться на тип T, а код может избежать приведения типов и компилятор гарантирует типобезопасность. Смотрите мой ответ для дальнейшего объяснения. - person Dave Costa; 14.01.2011
comment
@Dave Costa: я не вижу от вас ответа, но не имеет значения, помогает ли использование <T> другим методам. Мы говорим об этих методах, в которых, похоже, нет никакой пользы. - person Mark Peters; 14.01.2011
comment
@Марк - только что закончил свой ответ. Да, вы правы, что это не имеет никакого значения в этих объявлениях методов, хотя потенциально на этот параметр T можно ссылаться в теле реализации. Но я прочитал исходный вопрос как более общий. - person Dave Costa; 14.01.2011

? это просто подстановочный знак. Это означает, что метод будет принимать коллекцию любого типа.

<T> — это параметр типа для метода. По сути, это присвоение подстановочному знаку имени, на которое затем можно ссылаться в другом месте в объявлении и определении метода.

Лучшей иллюстрацией разницы было бы, если бы возвращаемый тип метода менялся в зависимости от типа, который был передан.

Скажем, вы начали с метода вроде

Object getRandomElement( Collection<?> c )

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

С параметром типа вы бы вместо этого написали

<T> T getRandomElement( Collection<T> c )

В этом случае, если вы вызываете этот метод с Collection<String>, компилятор знает, что он вернет String.

person Dave Costa    schedule 14.01.2011
comment
Хорошо сказано. Я думаю, что хорошим эмпирическим правилом является то, что параметр типа требуется только тогда, когда у вас есть параметризованный возвращаемый тип (например, Collections.emptyList()) или когда вам нужно сопоставить возвращаемый тип или другой параметр с параметризованным или универсальным параметром. - person Mark Peters; 14.01.2011

Используемый здесь <T> (в объявлении метода перед типом возвращаемого значения) является объявлением универсального типа. Вы можете определить новый общий тип для использования в методе: http://download.oracle.com/javase/tutorial/java/generics/genmethods.html

person Peter Knego    schedule 14.01.2011

Попробуйте скомпилировать без <T>.

По сути, он сообщает компилятору, что этот метод содержит дженерик. В первом примере это не требуется, потому что ? является особым случаем, а второй метод ссылается на тип, определенный в самом интерфейсе.

Кстати, в интерфейсе public не требуется. Методы в интерфейсе общедоступны по умолчанию, поэтому вы можете сэкономить немного времени на вводе текста.

person Mikezx6r    schedule 14.01.2011

Он объявляет общий тип T, используемый методом. В то время как общий тип E одинаков для всего интерфейса, T ограничен методом, для которого он объявлен.

person josefx    schedule 14.01.2011