Розбираємося, як у Kotlin працює підтипність в узагальнених типах: чому
List<Expense> можна безпечно використовувати як
List<Transaction>, але
MutableList<Expense> не можна «підняти» до
MutableList<Transaction>. На прикладах показуємо інваріантність контейнерів (
Box<T>), коваріантність read-only колекцій, помилки компілятора та практичні прийоми проєктування API (приймати
List, якщо не потрібно мутувати), а також інтуїцію на прикладі типів функцій
(Transaction) -> String.