Introduction
In the last episode, we mastered Supplier, the source that generates values from nothing.
Now, let's turn to UnaryOperator, a specialization of Function built for one very specific job: transforming a value into another value of the exact same type.
If Predicate decides, Function transforms, Consumer acts, and Supplier provides, then UnaryOperator refines. It takes something, and gives back more of the same kind, changed.
What Is UnaryOperator?
Here's the definition:
@FunctionalInterface
public interface UnaryOperator<T> extends Function<T, T> {
static <T> UnaryOperator<T> identity() {
return t -> t;
}
}
Input: an object of type T.
Output: another object, also of type T.
Purpose: express a transformation that never changes the type, only the value.
Why Use UnaryOperator?
You could always write this with a plain Function<T, T>:
Function<String, String> trim = String::trim;
That works, but it doesn't say anything extra about the shape of the transformation, a reader still has to check both generic parameters to confirm they match. UnaryOperator states the contract directly:
UnaryOperator<String> trim = String::trim;
This one-type guarantee matters most where the JDK itself expects it, like List.replaceAll.
Practical Examples
1. In-Place List Transformation
List<String> names = new ArrayList<>(List.of("filipe", "andré", "borba"));
UnaryOperator<String> capitalize = s -> s.substring(0, 1).toUpperCase() + s.substring(1);
names.replaceAll(capitalize);
// [Filipe, André, Borba]
2. Simple Same-Type Math
UnaryOperator<Integer> increment = n -> n + 1;
System.out.println(increment.apply(41)); // 42
3. Composition (Inherited from Function)
UnaryOperator<String> trim = String::trim;
UnaryOperator<String> upper = String::toUpperCase;
Function<String, String> pipeline = trim.andThen(upper);
System.out.println(pipeline.apply(" borba ")); // "BORBA"
4. Iterative Generation with Stream.iterate
UnaryOperator<Integer> doubleIt = n -> n * 2;
List<Integer> powersOfTwo = Stream.iterate(1, doubleIt)
.limit(5)
.toList();
System.out.println(powersOfTwo); // [1, 2, 4, 8, 16]
Real-World Patterns
-
In-Place Normalization: Trimming, upper/lower-casing, or sanitizing collections via
replaceAll. -
State Transitions: Modeling a value moving to the next state of the same type, e.g. an
Orderwith an updated status. -
Iterative Algorithms: Feeding
Stream.iterateor recursive-style computations that keep producing the same type. - Configuration Overrides: Applying a same-type patch or decorator to a config or builder object.
Best Practices
- Prefer Immutability: Return a new instance instead of mutating the input, especially for domain objects.
-
Reserve It for Same-Type Transforms: If input and output types differ, use
Function<T, R>instead, don't force the fit. -
Name for the Operation:
increment,trim,capitalizecommunicate intent better thanop.
Common Pitfalls
- Silent Mutation: Since T equals T, it's tempting to mutate the argument directly instead of returning a new value, breaking purity.
-
Forcing the Fit: Wrapping a
Function<T, R>asUnaryOperator<T>by ignoring a type mismatch defeats its purpose. -
Overusing in Long Compositions: A long chain of
andThencalls can become as unreadable as the imperative code it replaced.
Functional Analogy
Think of UnaryOperator as a polishing machine:
- Raw material T goes in.
- The machine reshapes it.
- What comes out is still T, just refined.
Conclusion
UnaryOperator is Function's disciplined sibling; it exists to make same-type transformations explicit and safe. Wherever a value needs to become a better version of itself, in place, in a pipeline, or across iterations, UnaryOperator is the right tool.
What's Next
In the next episode, we'll meet BinaryOperator, a step further: instead of transforming one value, it combines two values of the same type into one. Get ready to reduce.
Top comments (6)
It might be also fun in the future for you to detach slightly from java.
You are actually touching much more general topics which can be interesting to even Pythonistas etc.
Maybe 80% general and 20% heavily specific would be cool to pull off.
I mean for example, what you have written is ok, but have you ever considered the following:
So this unary maps dots from Set A to Set A.
You started touching sublist etc. to suggest its generality.
How about generating/traversing a tree level by level?
Have you considered approaching the N-Queens problem this way?
I mean you clearly wrote that Streams have to do something with recursion (sometimes).
The word - imo - you have been circling around is corecursion.
It is indeed insanely close to recursion, but it is much more closer to what you were jumping around here, once again imo.
But the TL;DR is: You could write articles which of course have your language in the spotlight at the end, but you could invite more viewers by offering them knowledge that is language agnostic, perhaps.
Yeah, I actually really liked your comment haha.
Just to clarify a few things, though:
I'm not really "discovering" these concepts through Java. I've known most of the Java/Streams-related stuff for years. I'm mostly just sharing things I already know, even if I'm deliberately keeping the discussion somewhat superficial. I've never really had the interest (or the need) to go much deeper into some of these topics.
That said, I do think the idea of approaching deeper/general CS concepts from a language-agnostic perspective is really interesting. I think it depends a lot on the purpose of the post/series, though. Sometimes the goal is simply to talk about something at a relatively superficial level and make it accessible. And that's fine too, perhaps that particular post simply isn't aimed at someone who already knows the underlying concepts deeply.
The part about lists, sublists, BFS, lazy sequences, tree traversal, etc. is actually SUPER interesting to me. That's very much the kind of direction I could see myself exploring in future posts/series. I already had some of those ideas in mind, actually.
And N-Queens is a great example too. That's also something I've had on my radar for a while. I really like the idea of approaching problems like that from the perspective of generation/traversal rather than immediately framing everything as "here's how to solve N-Queens in Java."
And the corecursion point was particularly interesting. I hadn't really been thinking about the whole thing under that name, so I'll definitely look into it more.
So yeah, ironically, I started reading your comment thinking "hmm, I'm not sure I agree with some of this" and ended up thinking "okay, wait, this actually gives me several ideas" lol.
I think the language-agnostic angle could be really cool for some of the future series, while I still want to keep some posts intentionally more language-specific and superficial. I don't necessarily think every post needs to go all the way down the abstraction rabbit hole.
I'm not interested in lecturing you. I'm not interested in what I know and seeing you repeat it. I'm interested in cool articles, so sometimes I have to throw in a little bit of a jab, to unsettle an author to make him try something crazy once in a while.
@borba but remember one thing besides all of this.
The name borba has been entered onto @highflyer910 Santa's Naughty List.
She is currently attending to other duties, but one day Thea Santa might show up under one of your christmas trees.
Hahahaha okay, now I get it 😂😂😂😂
So the goal was not to teach me, but to poke me hard enough that I start doing something slightly insane once in a while.
Fair enough. I actually like that and really appreciate.
I guess this means I have to start writing more provocative and slightly crazier stuff now. Maybe push the ideas further, play with some concepts I normally wouldn't bother exploring, and see where they go.
Because apparently I have a much more important problem to solve first:
I need to get Borba off Santa's Naughty List.
I don't know what Thea Santa has planned for me, but I'm not willing to find out by waking up one Christmas morning and finding her under my tree.
So yeah... expect some increasingly unhinged articles from me. 😂
🎄 I'm going to try to earn my way back onto the Nice List. 🎄
🎅
I leave you two alone for a while, and apparently I have a Naughty List now 😂
Careful, @borba. Increasingly unhinged articles might get you off the Naughty List... or move you even higher on it. I'm not sure yet 🎄:D