Подтвердить что ты не робот

Нужны ли генераторы подстановочных знаков?

например:

    public String add(Set<?> t){
    ...;
    }



    public <T> String add(Set<T> t){
    ...;
    }

Первый использует подстановочные дженерики; вторая - обычная форма общего метода. Какая разница?

В какой ситуации нам нужны подстановочные дженерики, а не нормальная форма дженериков?

4b9b3361

Ответ 1

Вот ситуация, когда требуются подстановочные знаки. Этот метод принимает List<List<?>>, который является списком списков. Метод может добавлять в него списки различных типов компонентов:

public void foo(List<List<?>> t) {
    t.add(new ArrayList<String>());
    t.add(new ArrayList<Integer>());
}

Вы не можете сделать это, используя общие параметры типа без подстановочных знаков. Например, следующее не работает:

public <T> void foo(List<List<T>> t) {
    t.add(new ArrayList<String>()); // does not compile
    t.add(new ArrayList<Integer>()); // does not compile
}

Ответ 2

Так как поддержка дженериков была добавлена, использование параметризованного типа без предоставления параметра типа обычно вызывает предупреждение компилятора. С другой стороны, бывают ситуации, когда вам все равно, что такое параметр типа (т.е. Вы не используете тип где-либо), или, что еще хуже, вы, возможно, не знаете, что T вообще, и используя <?>, вы можете просто выразить это, не вызывая предупреждения компилятора.

Возможный вариант использования для случая "не заботьтесь" (очень просто для краткости, но вы понимаете):

public void clearList(List<?> list) {
    list.clear();
}

Пример для случая "не знаю": фактическая подпись метода из класса Class:

static Class<?> forName(String className);

Здесь метод возвращает объект некоторого типа Class. Class является общим, но, конечно, вы не знаете тип, потому что он зависит от параметра className, который разрешен во время выполнения. Таким образом, вы не можете поместить T здесь, так как T не известно во время компиляции (даже для определенного сайта вызова), а использование только Class без параметра типа будет плохой практикой и вызовет предупреждение компилятора.

Ответ 3

Подстановочная форма - это когда вы не возражаете против того, какие типы объектов вы обрабатываете.

Форма generics позволяет добавлять ограничения на тип обработанных объектов.

Пример использования может быть следующим: общий репозиторий с методами add/update/remove, вы определяете общее поведение с использованием общего типа:

public class Repository<T>{
 public void add(T t){...}
 public void update(T t){...}
 public void remove(T t){...}
}

Затем, чтобы создать репозиторий для Apple и Banana, вы просто расширяете этот класс и заменяете T действительным типом:

public class AppleRepo extends Repository<Apple> {}
public class BananaRepo extends Repository<Banana> {}

Если общий репозиторий был объявлен как Repository<?>, это не было бы хорошо, потому что оно не ограничивается бананом, и вы не сможете использовать в нем Banana определенные методы без кастования объектов;

Также генерические средства позволяют вам выражать дополнительные ограничения, например

Repository<T extends Fruit>

позволяет ограничить общий класс репозитория плодами. И вы сможете совершать вызовы методов Fruit в своем коде.

Ответ 4

Там нет разницы в вызове методов.

Во втором методе (add(Set<T>)) вы можете создавать переменные типа T:

public <T> String add(Set<T> t){
    T item = t.iterator().next();
    //....
}

Это дает вам дополнительную проверку типов. В первом методе вы остаетесь с помощью Object.