48. Получить первый элемент списка с сохранением типа
Условие задачи:
Необходимо реализовать универсальный метод, который получает первый элемент списка и сохраняет его тип.
Элементы списка могут иметь произвольный тип. Решение должно быть типобезопасным и не требовать явного приведения типов.
Код:
class A {
public static T getFirstElementOfList(List<T> list) {
return list.size() > 0 ? list.get(0) : null;
}
}
Также необходимо объяснить использование wildcard:
method(List<? extends T> producer)
method(List<? super T> consumer)
Спойлеры к решению
Подсказки
<T>.💡 Вместо
null можно возвращать Optional<T>.💡 Если список предоставляет значения типа
T, используй ? extends T.💡 Если в список записываются значения типа
T, используй ? super T.💡 Это правило называется PECS: Producer Extends, Consumer Super.
Решение
Типобезопасный вариант с Optional:
class A {
public static <T> Optional<T> firstOf(
List<? extends T> list
) {
if (list == null || list.isEmpty()) {
return Optional.empty();
}
return Optional.ofNullable(list.get(0));
}
}
Пример использования:
List<Integer> numbers = List.of(1, 2, 3);
Optional<Integer> first = A.firstOf(numbers);
first.ifPresent(System.out::println);
Если по контракту допустимо возвращать null:
public static <T> T firstOrNull(
List<? extends T> list
) {
if (list == null || list.isEmpty()) {
return null;
}
return list.get(0);
}
Пример producer:
public static <T> void readValues(
List<? extends T> producer
) {
for (T value : producer) {
System.out.println(value);
}
}
Из List<? extends T> можно безопасно читать элементы как T, но нельзя добавлять новые значения, поскольку конкретный подтип неизвестен.
Пример consumer:
public static <T> void addValue(
List<? super T> consumer,
T value
) {
consumer.add(value);
}
В List<? super T> можно безопасно добавлять значения типа T, но результат чтения доступен только как Object.
Пример с иерархией числовых типов:
List<Integer> integers = List.of(1, 2, 3);
List<Number> numbers = new ArrayList<>();
List<Object> objects = new ArrayList<>();
Optional<Integer> firstInteger = firstOf(integers);
Optional<Number> firstNumber = firstOf(integers);
addValue(numbers, 10);
addValue(objects, 10);
Integer и AtomicInteger являются разными наследниками класса Number:
Object
└── Number
├── Integer
└── AtomicInteger