Java's UnaryOperator<T> is one of the functional interfaces in java.util.function. Its purpose is simple: accept a value of type T and return another value of the same type.
For example, a UnaryOperator<String> accepts a String and returns a String. A UnaryOperator<Integer> accepts an Integer and returns an Integer.
The UnaryOperator interface
UnaryOperator<T> extends Function<T,T>. It does not declare a new abstract apply() method of its own; it inherits apply(T) from Function. Because the input and result types are the same, UnaryOperator is a convenient specialization of Function.
@FunctionalInterface
public interface UnaryOperator<T> extends Function<T, T> {
// Inherited from Function<T, T>:
// T apply(T value);
}For example, an operator could remove every nonnumeric character from a string. The input is a String and the result is also a String, which makes the operation a natural fit for UnaryOperator<String>.
Definition of the term unary.
Implementation of the UnaryOperator example
You can implement UnaryOperator with a normal class, although the interface itself was introduced with Java 8. In this example, the type argument String means both the input to apply() and its return value must be a String.
package com.mcnz.lambda;
import java.util.function.UnaryOperator;
public class UnaryOperatorTest {
public static void main(String[] args) {
UnaryOperator<String> extensionAdder =
text -> text + ".txt";
String result = extensionAdder.apply("example-function");
System.out.println(result);
}
}Java can infer the parameter type, and parentheses are optional for a lambda with one inferred parameter. The operator can therefore be reduced to a single expression:
UnaryOperator<String> extensionAdder = text -> text + ".txt";Where Java uses UnaryOperator
UnaryOperator appears naturally in APIs that repeatedly transform a value without changing its type. One example is Stream.iterate(), whose second argument calculates the next value from the current one:
static <T> Stream<T> iterate(
T seed,
UnaryOperator<T> next
)For example, this operator generates the next integer by adding one to the current value:
UnaryOperator<Integer> increment = number -> number + 1;
Stream.iterate(1, increment)
.limit(5)
.forEach(System.out::println);The output is 1 through 5.
Another common example is List.replaceAll(UnaryOperator<E>):
List<String> names =
new ArrayList<>(List.of("duke", "james", "gosling"));
names.replaceAll(String::toUpperCase);
System.out.println(names);A useful rule of thumb is simple: use Function<T,R> when the input and output types may differ, and use UnaryOperator<T> when they must be the same type.