Complete Java UnaryOperator interface tutorial covering all methods with examples. Learn about functional programming in Java.
Last modified: April 16, 2025
The java.util.function.UnaryOperator interface represents an operation on a single operand that produces a result of the same type. It extends the Function interface and is a functional interface with method apply. UnaryOperator is commonly used for transformations where input and output types match.
UnaryOperator is part of Java’s functional programming utilities added in Java 8. It specializes Function for cases where the input and output types are identical. This simplifies type declarations and provides type safety.
UnaryOperator interface contains one abstract method inherited from Function and several static methods. The key method apply performs the operation on the input. Static methods provide useful operator instances.
@FunctionalInterface public interface UnaryOperator<T> extends Function<T, T> { static <T> UnaryOperator<T> identity(); }
The code above shows the structure of UnaryOperator interface. It extends Function with identical input and output types. The identity method returns a UnaryOperator that always returns its input argument.
The simplest way to use UnaryOperator is with lambda expressions. We define how to transform the input to output of the same type. The example squares integers.
Main.java
package com.zetcode;
import java.util.function.UnaryOperator;
public class Main {
public static void main(String[] args) {
// Define a UnaryOperator that squares integers
UnaryOperator<Integer> square = x -> x * x;
// Apply the operator
System.out.println("Square of 5: " + square.apply(5));
System.out.println("Square of 12: " + square.apply(12));
// UnaryOperator using method reference
UnaryOperator<String> toUpper = String::toUpperCase;
System.out.println("Uppercase: " + toUpper.apply("hello"));
}
}
This example demonstrates basic UnaryOperator usage with lambda and method reference. The square operator takes Integer and returns Integer. The toUpper operator transforms strings to uppercase. Both maintain input-output type.
UnaryOperator inherits composition methods from Function. andThen and compose allow chaining operations while maintaining type consistency. This creates powerful transformation pipelines.
Main.java
package com.zetcode;
import java.util.function.UnaryOperator;
public class Main {
public static void main(String[] args) {
// First operator increments by 1
UnaryOperator<Integer> increment = x -> x + 1;
// Second operator multiplies by 2
UnaryOperator<Integer> doubler = x -> x * 2;
// Compose the operators
UnaryOperator<Integer> incrementThenDouble = increment.andThen(doubler);
UnaryOperator<Integer> doubleThenIncrement = doubler.andThen(increment);
System.out.println("Increment then double 5: " + incrementThenDouble.apply(5));
System.out.println("Double then increment 5: " + doubleThenIncrement.apply(5));
}
}
This example shows UnaryOperator composition. We chain mathematical operations while maintaining Integer type throughout. The order of operations affects the final result significantly.
The UnaryOperator.identity method returns an operator that always returns its input argument unchanged. It’s useful as a default or neutral operation in transformation pipelines.
Main.java
package com.zetcode;
import java.util.function.UnaryOperator;
public class Main {
public static void main(String[] args) {
// Identity operator
UnaryOperator<String> identity = UnaryOperator.identity();
System.out.println("Identity applied: " + identity.apply("test"));
// Practical use with optional transformation
UnaryOperator<String> transformer = someCondition()
? String::toUpperCase
: UnaryOperator.identity();
System.out.println("Result: " + transformer.apply("conditional"));
}
private static boolean someCondition() {
return false;
}
}
This example demonstrates UnaryOperator.identity. The identity operator returns its input unchanged. It serves as a neutral element when conditional transformation is needed but sometimes no operation is required.
UnaryOperator works naturally with Java Streams for in-place transformations. The map operation accepts a UnaryOperator when input and output types match. This enables clean data processing pipelines.
Main.java
package com.zetcode;
import java.util.Arrays; import java.util.List; import java.util.function.UnaryOperator; import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// UnaryOperator to square numbers
UnaryOperator<Integer> square = x -> x * x;
// Apply operator in stream
List<Integer> squaredNumbers = numbers.stream()
.map(square)
.collect(Collectors.toList());
System.out.println("Original: " + numbers);
System.out.println("Squared: " + squaredNumbers);
}
}
This example shows UnaryOperator usage in Streams. We define a squaring operator and apply it to each stream element via map. The result is a new list with transformed values while maintaining the Integer type throughout.
UnaryOperator is particularly useful for string transformations where the operation preserves the String type. Common examples include trimming, case conversion, and formatting.
Main.java
package com.zetcode;
import java.util.function.UnaryOperator;
public class Main {
public static void main(String[] args) {
// Trim and capitalize string
UnaryOperator<String> trimAndCapitalize = s -> {
String trimmed = s.trim();
return trimmed.substring(0, 1).toUpperCase() +
trimmed.substring(1).toLowerCase();
};
System.out.println("Formatted: " + trimAndCapitalize.apply(" hELLO "));
// Repeat string three times
UnaryOperator<String> tripler = s -> s + s + s;
System.out.println("Tripled: " + tripler.apply("hi"));
}
}
This example demonstrates String manipulation with UnaryOperator. We create operators that clean and format strings while maintaining the String type. The operators can be reused across different string processing scenarios.
Java provides specialized UnaryOperator variants for primitive types to avoid boxing overhead. These include IntUnaryOperator, LongUnaryOperator, and DoubleUnaryOperator.
Main.java
package com.zetcode;
import java.util.function.IntUnaryOperator; import java.util.function.DoubleUnaryOperator;
public class Main {
public static void main(String[] args) {
// IntUnaryOperator example
IntUnaryOperator negate = x -> -x;
System.out.println("Negated: " + negate.applyAsInt(42));
// DoubleUnaryOperator example
DoubleUnaryOperator celsiusToFahrenheit = c -> (c * 9/5) + 32;
System.out.println("20°C in Fahrenheit: " +
celsiusToFahrenheit.applyAsDouble(20));
}
}
This example shows specialized UnaryOperator variants. IntUnaryOperator works with int primitives directly, avoiding Integer boxing. Similarly, DoubleUnaryOperator handles double primitives efficiently.
Java UnaryOperator Interface Documentation
In this article, we’ve covered the essential methods and features of the Java UnaryOperator interface. Understanding these concepts is crucial for type-safe transformations in functional programming and stream processing.
My name is Jan Bodnar, and I am a dedicated programmer with many years of experience in the field. I began writing programming articles in 2007 and have since authored over 1,400 articles and eight e-books. With more than eight years of teaching experience, I am committed to sharing my knowledge and helping others master programming concepts.
List all Java tutorials.