Сейчас мы приступим к изучению одной из самых сложных частей в обобщениях - wildcards.
Вспомним как мы создавали стандарный generic класс:
class ArrayList<T> {
...
}
в данном случае T - это type parameter класса ArrayList.
Создание экземпляра такого класса выглядит следующим образом:
ArrayList<Number> arrayList = new ArrayList<Number>();
Все четко, при объявлении параметр T задаете как Number.
Теперь вы хотите написать функцию которая суммирует все элементы коллекции:
public static double sum(ArrayList<Number> numbers){
double summ = 0.0;
for(Number curNumber : numbers)
summ += curNumber.doubleValue();
return summ;
}
Но когда мы захотим передать в это функцию коллекцию вида ArrayList
public static double sum(ArrayList<? extends Number> numbers){
...
}
Т.е. метод принимает тип ArrayList который параметризирован Number или наследником Number.