Generics - makstron/info GitHub Wiki
-
Коваріантність - це збереження ієрархії успадкування вихідних типів у похідних типах у тому самому порядку. Наприклад, якщо
Cat
- це підтипAnimal
, тоList<Cat>
- це підтипList<Animal>
. Отже, з урахуванням принципу підстановки можна виконати таке присвоєння:List<Animal> = List<Cat>
-
Контраваріантність - це звернення ієрархії вихідних типів на протилежну в похідних типах. Наприклад, якщо
Cat
- це підтипAnimal
, тоList<Animal>
- це підтипList<Cat>
. Отже, з урахуванням принципу підстановки можна виконати таке присвоєння:List<Cat> = List<Animal>
-
Інваріантність - відсутність успадкування між похідними типами. Якщо
Cat
- це підтипAnimal
, тоList<Cat>
не є підтипомList<Animal>
іList<Animal>
не є підтипомList<Cat>
.
Дженеріки інваріантні
Generics не завжди інваріантні
PECS (Producer Extends Consumer Super)
class A { }
class B extends A { }
class C extends B { }
// Container
class S<V> {
private V value;
public V get() { return value; }
public void set(V value) { this.value = value; }
}
extends - дозволяє тільки отримувати НЕ присвоювати
get()
- Дозволено. Поверне щось, що є спадкоємцем B
. Тобто що б метод get
не повернув - це щось можна записати в змінну типу B
.
S<? extends B> s;
B v = s.get(); // Значення типу ? extends B завжди можна записати в змінну типу B
set()
- заборонено. В s
може міститись колекція обєктів нащадків В
наприклад С
. Обєкт В
не можна кастувати до його нащадка С
. Відповідно в колекцію s
не модна присвоїти В
.
S<? extends B> s = new S<C>(); // можна присвоїти `С` в `В` тому що `extends B`
s.set(new B()); // Помилка - значення типу B не може бути передано як параметр типу C
super - дозволяє тільки присвоювати НЕ отримувати
...........