Работали ли вы с дженериками в Java?
Что проверяют
Понимаете ли вы, что дженерики живут только в компиляторе и стираются в байт-коде, и умеете ли объявлять границы типа. В андроид-коде это встречается ежедневно: базовые адаптеры списков, обёртка над результатом сети, общий фрагмент с привязкой разметки.
Как отвечать
Дженерик это параметр типа у класса, интерфейса или метода. Компилятор проверяет совместимость на этапе сборки, поэтому приведения типов и падения в рантайме исчезают. Полезно сразу показать ограничение и подстановочный тип, а не только ArrayList<String>:
static <T extends Comparable<T>> T max(List<? extends T> items) {
T best = items.get(0);
for (T item : items) if (item.compareTo(best) > 0) best = item;
return best;
}Дальше про стирание: после компиляции List<String> и List<Integer> это один и тот же класс, поэтому нельзя написать new T(), создать new T[] или проверить параметр через instanceof. Отсюда знакомый андроид-приём: тип передают руками отдельным аргументом, как Class<T> в старом ViewModelProvider.get(MyViewModel.class). В Kotlin ту же задачу закрывает inline функция с reified параметром, там тип доступен внутри тела. Если проект на Kotlin, упомяните вариантность через out и in: это то же самое, что подстановочные типы Java, только объявленное один раз на самом классе.
Где спотыкаются
Не знают про стирание и обещают определить тип в рантайме. Следом не могут объяснить, почему два метода с параметрами List<String> и List<Integer> не компилируются: после стирания сигнатуры совпадают.
Путают List<Object>, List<?> и List<? extends Number>. В коллекцию с верхней границей ничего нельзя положить, кроме null, потому что реальный тип элемента компилятору неизвестен.
Работают с сырым типом. Объявление без параметра отключает проверки для всего класса, а не для одной строки, и превращает ошибку сборки в падение при приведении. Туда же ковариантность массивов: Object[] принимает чужой элемент и роняет код уже на исполнении.