Специализации функциональных интерфейсов в Java - Вопросы

Всего: 21 вопросов

1. 

Перечислите основные функциональные интерфейсы пакета java.util.function.

Predicate<T>, Consumer<T>, Supplier<T>, Function<T, R> и UnaryOperator<T>. От них образованы бинарные и примитивные специализации — всего 43 интерфейса в пакете.

2. 

Для чего нужны бинарные специализации интерфейсов Predicate, Consumer, Function и UnaryOperator?

Интерфейсы Predicate, Consumer, Function и UnaryOperator описывают операцию с одним аргументом. Их бинарные версии принимают два аргумента. Семантика та же, отличается только количество параметров.

3. 

Как называется бинарная версия интерфейса Consumer и каков её функциональный дескриптор?

BiConsumer<T, U> с дескриптором (T, U) -> void. Абстрактный метод — void accept(T t, U u).

4. 

Как называется бинарная версия интерфейса Function и каков её функциональный дескриптор?

BiFunction<T, U, R> с дескриптором (T, U) -> R. Абстрактный метод — R apply(T t, U u).

5. 

Как называется бинарная версия интерфейса Predicate и каков её функциональный дескриптор?

BiPredicate<T, U> с дескриптором (T, U) -> boolean. Абстрактный метод — boolean test(T t, U u).

6. 

Как называется бинарная версия интерфейса UnaryOperator и каков её функциональный дескриптор?

BinaryOperator<T> с дескриптором (T, T) -> T. Интерфейс наследует BiFunction<T, T, T> и добавляет статические методы minBy() и maxBy().

7. 

Для чего нужны примитивные специализации интерфейсов Predicate, Consumer, Function, Supplier и UnaryOperator?

Примитивные специализации позволяют избежать операций автоупаковки и распаковки, когда входные или выходные значения примитивны. Обобщения в Java работают только со ссылочными типами, поэтому Consumer<Integer> на каждом вызове создаёт объект-обёртку, а IntConsumer — нет.

8. 

Перепишите пример с использованием примитивной специализации:

Consumer<Integer> ic = i -> System.out.println(++i);
ic.accept(8);
ic.accept(9);
IntConsumer ic = i -> System.out.println(++i);
ic.accept(8);
ic.accept(9);

Автоупаковка int в Integer при каждом вызове больше не выполняется.

9. 

Перечислите примитивные специализации функционального интерфейса Consumer.

IntConsumer — void accept(int)

LongConsumer — void accept(long)

DoubleConsumer — void accept(double)

10. 

Перечислите примитивные специализации интерфейса Function, которые принимают примитив и возвращают обобщённый тип.

IntFunction<R> — R apply(int)

LongFunction<R> — R apply(long)

DoubleFunction<R> — R apply(double)

11. 

Перечислите примитивные специализации интерфейса Function, которые принимают обобщённый тип и возвращают примитив.

ToIntFunction<T> — int applyAsInt(T)

ToLongFunction<T> — long applyAsLong(T)

ToDoubleFunction<T> — double applyAsDouble(T)

12. 

Перечислите примитивные специализации интерфейса Function, которые и принимают, и возвращают примитив.

IntToDoubleFunction, IntToLongFunction

LongToDoubleFunction, LongToIntFunction

DoubleToIntFunction, DoubleToLongFunction

Версий вида IntToIntFunction нет — вместо них используется IntUnaryOperator.

13. 

Перечислите примитивные специализации функционального интерфейса Supplier.

BooleanSupplier — boolean getAsBoolean()

IntSupplier — int getAsInt()

LongSupplier — long getAsLong()

DoubleSupplier — double getAsDouble()

Это единственное семейство, где есть специализация для boolean.

14. 

Перечислите примитивные специализации функционального интерфейса UnaryOperator.

IntUnaryOperator — int applyAsInt(int)

LongUnaryOperator — long applyAsLong(long)

DoubleUnaryOperator — double applyAsDouble(double)

У всех трёх есть default-методы compose() и andThen() и статический identity().

15. 

Перечислите примитивные специализации функционального интерфейса BinaryOperator.

IntBinaryOperator — int applyAsInt(int, int)

LongBinaryOperator — long applyAsLong(long, long)

DoubleBinaryOperator — double applyAsDouble(double, double)

16. 

Перечислите примитивные специализации функционального интерфейса BiConsumer.

ObjIntConsumer<T> — void accept(T, int)

ObjLongConsumer<T> — void accept(T, long)

ObjDoubleConsumer<T> — void accept(T, double)

Первый аргумент остаётся объектным, примитивным становится только второй.

17. 

Перечислите примитивные специализации функционального интерфейса BiFunction.

ToIntBiFunction<T, U> — int applyAsInt(T, U)

ToLongBiFunction<T, U> — long applyAsLong(T, U)

ToDoubleBiFunction<T, U> — double applyAsDouble(T, U)

Примитивным здесь становится только результат, оба аргумента остаются обобщёнными.

18. 

Перечислите примитивные специализации функционального интерфейса Predicate. Есть ли примитивная версия у BiPredicate?

IntPredicate — boolean test(int)

LongPredicate — boolean test(long)

DoublePredicate — boolean test(double)

У BiPredicate примитивных специализаций нет: интерфейсов вида IntBiPredicate или ObjIntPredicate в пакете не существует.

19. 

Как по названию интерфейса из java.util.function восстановить его сигнатуру?

Имена строятся по шаблону:

Int / Long / Double в начале — тип входного параметра: IntPredicate — boolean test(int).

To + тип — тип возвращаемого значения: ToIntFunction<T> — int applyAsInt(T).

Тип + To + тип — примитивны и вход, и выход: IntToDoubleFunction — double applyAsDouble(int).

Obj + тип — объект плюс примитив: ObjIntConsumer<T> — void accept(T, int).

Bi в начале — два объектных аргумента: BiFunction<T, U, R> — R apply(T, U).

UnaryOperator / BinaryOperator — все типы совпадают: IntBinaryOperator — int applyAsInt(int, int).

Если результат примитивный, к имени метода добавляется суффикс As + тип: applyAsInt, getAsDouble. У test() и accept() суффикса нет — их результат и так фиксирован.

20. 

Лямбды каких функциональных интерфейсов ожидают методы Stream и IntStream: mapToInt(), map(), mapToObj(), filter(), forEach(), reduce()?

Stream<T>.mapToInt() — ToIntFunction<T>, переход к IntStream.

IntStream.map() — IntUnaryOperator.

IntStream.mapToObj() — IntFunction<R>, возврат к Stream<R>.

IntStream.filter() — IntPredicate.

IntStream.forEach() — IntConsumer.

IntStream.reduce() — IntBinaryOperator.

Дополнительно: Map.forEach() ждёт BiConsumer<K, V>, Map.merge() — BiFunction<V, V, V>, List.replaceAll() — UnaryOperator<E>.

21. 

Для каких примитивных типов существуют специализации и наследуют ли они обобщённые интерфейсы?

Специализации есть только для int, long и double (плюс BooleanSupplier для boolean). Для float, byte, short и char отдельных интерфейсов нет — используются int- и double-версии с расширяющим преобразованием.

Примитивные специализации не наследуют обобщённые интерфейсы: IntPredicate не является наследником Predicate<Integer>, а IntBinaryOperator — наследником BiFunction. Это независимые типы, присвоить переменную одного другому нельзя.

Страница 1 из 1