Generics и функциональное
Тема дорожной карты · Java
Generics и функциональное программирование в Java пересекаются в основе современного API-дизайна, введённого в Java 8, где типобезопасная параметризация Java Generics придаёт функциональным интерфейсам — Function<T,R>, Predicate<T>, Supplier<T>, Consumer<T> — их безопасность типов на этапе компиляции и возможность повторного использования. Без Java Generics Stream API потребовал бы непроверяемых приведений типов на каждом этапе конвейера: stream.map(Function<T,R>) является обобщённым по обоим входному и выходному типам, и компилятор проверяет, что такие цепочки вызовов, как .map(String::length).filter(n -> n > 3), сохраняют корректные типы на протяжении всего конвейера без накладных расходов во время выполнения. Wildcard-ы в Generics существенны для написания гибких утилитных методов, компонующих функциональные интерфейсы: <T> List<T> filterList(List<? extends T> list, Predicate<? super T> pred) принимает любой совместимый список и предикат без потери информации о типах, следуя принципу PECS. Generics и функциональное программирование также сочетаются в интерфейсе Comparator — Comparator.comparing(Function<T, U> keyExtractor) является обобщённым фабричным методом, возвращающим Comparator<T>, а его метод .thenComparing() связывает компараторы с полным выводом обобщённых типов. Владение взаимодействием Java Generics и Functional Interfaces необходимо для чтения и написания идиоматического Java 11–21 кода, особенно во фреймворках: Spring's ResponseEntity<T>, Mockito's Answer<T>, AssertJ's AbstractAssert<SELF,ACTUAL> и весь пакет java.util.stream.
Как это работает
Generics и функциональное покрывает generics (параметры типа: class Box<T>, List<String>, Map<K, V>), wildcards (? extends, ? super, правило PECS), функциональные интерфейсы (один abstract-метод: Function, Consumer, Supplier, Predicate), лямбды (x -> x.toUpperCase()), method references (String::toUpperCase), Stream API. Type erasure означает, что generic-инфа исчезает на рантайме — работает на compile-time, но List<String>.class тот же, что List<Integer>.class.
Когда применять
Stream API — для трансформаций (.filter().map().collect()) когда intent яснее цикла. Generics — в API библиотек чтобы протолкнуть type-safety вызывающим. Применяйте PECS (Producer Extends, Consumer Super) при проектировании wildcards. Не over-engineer — Function<? super T, ? extends R> иногда правильный ответ, иногда запах.
Типичные ошибки
Ловушки Generics и функциональное: List rawList = new ArrayList() (raw-типы — обходят generics, ломают type-safety); злоупотребление Stream .peek() для side-эффектов (это для дебага — forEach если нужны side-эффекты); stateful-лямбды (parallel streams + mutable shared state = race); instanceof + cast вместо pattern matching (Java 21+).