— Привет! Я продолжу лекцию Элли про Generic’и. Готов слушать?

— Ага.

— Тогда начинаем.

Факт первый. У методов класса тоже могут быть свои типы-параметры.

— Да, я знаю.

— Нет, я имею ввиду именно свои типы-параметры:

Пример
class Calculator
{
  T add(T a, T b); //сложить
  T sub(T a, T b); //отнять
  T mul(T a, T b); //умножить
  T div(T a, T b); //делить
}

Это типы-параметры именно метода(ов). У класса параметров нет. Можно даже объявить методы статическими и вызывать их без использования объекта.

— Ясно. Смысл типов-параметров в методах такой же, как и в классах?

— Ага. Но есть и кое-что новое.

Как ты уже знаешь, в описании типа можно использовать wildcard. Тогда представь себе ситуацию:

Пример 1
public void doSomething(List<? extends MyClass> list) 
{
 for(MyClass object : list)
 { 
  System.out.println(object.getState()); //тут все работает отлично.
 }
}

А вот, что случится, если мы захотим добавить в коллекцию новый элемент:

Пример 2
public void doSomething(List<? extends MyClass> list) 
{
 list.add(new MyClass()); //ошибка!
}

Дело в том, что в общем случае в метод doSomething можно передать List с типом элементов не MyClass, а любой из наследников MyClass. А в такой список заносить объекты MyClass уже нельзя!

— Ага. И что же делать?

— Ничего. Прямо в этой ситуации – ничего не сделаешь. Но это дало разработчикам Java повод для размышлений. И они придумали новое ключевое слово – super.

Выглядит его использование практически так же:

List<? super MyClass> list

Но между extends и super есть существенное различие.

«? extends T» обозначает, что класс должен быть наследником T.

«? super T» обозначает, что класс должен быть предком T.

— Ух ты. А где это используется?

— «? super T» используется, когда метод собирается добавлять в коллекцию объект типа T. Тогда это может быть коллекция типа T или любого типа-предка T.

— Ага. Ссылку на объект типа T можно же присвоить любому родительскому типу для T.

— Честно говоря – этот подход используется не очень часто. Тем более, что у него есть и обратная сторона. Пример:

Примеры
public void doSomething(List<? super MyClass> list)
{
 for(MyClass object : list) //ошибка!
 { 
  System.out.println(object.getState()); 
 }
}
public void doSomething(List<? super MyClass> list)
{
 list.add(new MyClass()); //тут все работает отлично.
}

Теперь не работает первый пример.

Т.к. коллекция list может быть даже List<Object> (Object самый верхний родитель MyClass), то фактически мы пишем такой код, а так писать нельзя:

Пример 1
List<Object> list; 

for(MyClass object : list) //ошибка!
{ 
 System.out.println(object.getState()); 
}

— Ясно. Спасибо за интересную лекцию.

— Пожалуйста.