Интерфейс UnaryOperator в Java - Вопросы

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

1. 

Когда используется интерфейс UnaryOperator?

UnaryOperator<T> — встроенный функциональный интерфейс из пакета java.util.function (Java SE 8). Он описывает операцию над одним операндом, результат которой имеет тот же тип, что и операнд: функциональный дескриптор T -> T. Интерфейс расширяет Function<T, T> и обычно записывается лямбда-выражением или ссылкой на метод, которые передают в другой метод как аргумент. Типичные места применения в JDK: List.replaceAll(), Stream.iterate(), AtomicReference.updateAndGet().

2. 

Используйте UnaryOperator в лямбда-выражении, чтобы привести строку к верхнему регистру.

Лямбда реализует единственный абстрактный метод T apply(T t), унаследованный от Function:

UnaryOperator<String> toUpper = s -> s.toUpperCase();
System.out.println(toUpper.apply("examclouds")); // EXAMCLOUDS

// то же самое короче — через ссылку на метод
UnaryOperator<String> toUpperRef = String::toUpperCase;
System.out.println(toUpperRef.apply("java")); // JAVA
3. 

Какой статический метод объявлен в интерфейсе UnaryOperator?

Единственный собственный член интерфейса — статический метод static <T> UnaryOperator<T> identity(). Он возвращает оператор t -> t, отдающий аргумент без изменений, и удобен как значение по умолчанию вместо проверок на null:

UnaryOperator<String> safe = (formatter == null) ? UnaryOperator.identity() : formatter;
System.out.println(UnaryOperator.identity().apply("Java")); // Java

От Function.identity() он отличается только статическим типом результата: UnaryOperator<T> вместо Function<T, T>.

4. 

Чем UnaryOperator отличается от Function и почему Function нельзя передать в List.replaceAll()?

Function<T, R> преобразует значение одного типа в значение другого (T -> R), а UnaryOperator<T> — частный случай, у которого типы аргумента и результата совпадают (T -> T). Наследование одностороннее: UnaryOperator<T> расширяет Function<T, T>, но не наоборот. Поэтому оператор можно передать всюду, где ждут функцию (например, в Stream.map()), а вот функцию туда, где по контракту нужен оператор, — нельзя.

Function<String, String> trim = String::trim;
List<String> names = new ArrayList<>(List.of("  ann  ", " bob"));

names.replaceAll(trim);        // не компилируется
names.replaceAll(trim::apply); // компилируется: ссылка на метод создаёт оператор
5. 

Что возвращает метод andThen() у интерфейса UnaryOperator?

Function<T, V>, а не UnaryOperator<T>. Интерфейс UnaryOperator не переопределяет ни andThen(), ни compose() — оба метода унаследованы от Function вместе с исходным типом возвращаемого значения, поэтому композиция двух операторов даёт обычную функцию. Если нужен именно оператор, соберите композицию лямбдой вручную:

UnaryOperator<String> trim = String::trim;

Function<String, String> f = trim.andThen(String::toUpperCase);        // так можно
UnaryOperator<String> op = s -> trim.apply(s).toUpperCase();           // а так получится оператор

У примитивных вариантов (IntUnaryOperator, LongUnaryOperator, DoubleUnaryOperator) поведение другое: их andThen() и compose() сохраняют исходный тип.

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