— Привіт! Давай продовжимо лекцію про 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()); 
}

— Зрозуміло. Дякую за цікаву лекцію.

— Будь ласка.