Интерфейс UnaryOperator в Java - Вопросы
Всего: 5 вопросов
1. Когда используется интерфейс UnaryOperator?
Когда используется интерфейс UnaryOperator?
UnaryOperator<T> — встроенный функциональный интерфейс из пакета java.util.function (Java SE 8). Он описывает операцию над одним операндом, результат которой имеет тот же тип, что и операнд: функциональный дескриптор T -> T. Интерфейс расширяет Function<T, T> и обычно записывается лямбда-выражением или ссылкой на метод, которые передают в другой метод как аргумент. Типичные места применения в JDK: List.replaceAll(), Stream.iterate(), AtomicReference.updateAndGet().
2. Используйте UnaryOperator в лямбда-выражении, чтобы привести строку к верхнему регистру.
Используйте 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?
Какой статический метод объявлен в интерфейсе 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()?
Чем 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?
Что возвращает метод 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() сохраняют исходный тип.