Functional Composition in Java
Functional programming isn’t only about passing functions around. We can also combine small functions to create larger functions.
This is called functional composition.
Functional composition = combining functions to produce a new function.
Think of functions as building blocks:
f1 → f2 → f3 → ResultInstead of writing one large function, we create small reusable functions and compose them.
1. Composing Function Objects
Suppose we have a Product and want to produce a formatted price message.
There are two transformations:
Product
│
▼
BigDecimal
│
▼
StringWe can represent each transformation independently.
Product → Price
Function<Product, BigDecimal> productToPrice = Product::getPrice;Shape:
Product → BigDecimalPrice → Message
Function<BigDecimal, String> priceToMessage =
price -> String.format("Price: %.2f", price);Shape:
BigDecimal → StringNow we have two small functions:
Product ──► productToPrice ──► BigDecimal
│
▼
priceToMessage
│
▼
StringInstead of executing them separately, we can compose them.
2. andThen()
Function provides the andThen() method:
Function<Product, String> productToMessage =
productToPrice.andThen(priceToMessage);Read this naturally:
Convert the product to price, and then convert the price to a message.
The flow is:
Product
│
▼
productToPrice
│
▼
BigDecimal
│
▼
priceToMessage
│
▼
StringMathematically:
f.andThen(g)x → f(x) → g(f(x))The result is a completely new function:
Function<Product, String>which performs both transformations.
3. Using the Composed Function
Previously, we might have written:
findProduct(products, predicate)
.map(Product::getPrice)
.map(price -> String.format("Price: %.2f", price))
.ifPresentOrElse(
System.out::println,
() -> System.out.println("Product not available")
);After composing the transformations:
Function<Product, String> productToMessage =
productToPrice.andThen(priceToMessage);we can simply use:
findProduct(products, predicate)
.map(productToMessage)
.ifPresentOrElse(
System.out::println,
() -> System.out.println("Product not available")
);So:
Two transformations
↓
Compose them
↓
One reusable FunctionThis becomes especially useful when the same transformation pipeline is needed in multiple places.
4. compose() vs andThen()
Function also provides:
compose()It performs the same idea, but the order is reversed.
With andThen():
f.andThen(g)execution is:
f → gSo:
g(f(x))With compose():
g.compose(f)execution is also:
f → gbecause the function passed to compose() executes first.
Therefore, these are equivalent:
productToPrice.andThen(priceToMessage)and:
priceToMessage.compose(productToPrice)Both produce:
Product → BigDecimal → StringEasy Memory Trick
f.andThen(g)
↓
f FIRST
g SECOND
f.compose(g)
↓
g FIRST
f SECONDThink:
andThen()reads in execution order.
That usually makes it easier to understand.
5. Composing Predicate Objects
Functional composition isn’t limited to Function.
Predicate also provides methods for combining conditions.
Suppose we want to find a product that is:
FOOD + CHEAPInstead of writing one large predicate, create two small ones.
Get Suraj’s stories in your inbox
Join Medium for free to get updates from this writer.
Is it food?
Predicate<Product> isFood =
product -> product.getCategory() == Category.FOOD;Is it cheap?
Predicate<Product> isCheap =
product -> product.getPrice().compareTo(priceLimit) < 0;Each predicate has one responsibility:
Product ──► isFood ──► true / false
Product ──► isCheap ──► true / falseNow we can combine them.
6. Predicate.and()
To require both conditions:
Predicate<Product> isFoodAndCheap = isFood.and(isCheap);
Conceptually:
┌──► isFood ────► true?
Product ─────┤
└──► isCheap ───► true?
│
▼
BOTH true?
│
▼
trueWe can then pass the combined predicate anywhere a Predicate<Product> is expected:
Optional<Product> result = findProduct(products, isFoodAndCheap);The caller doesn’t need to know that the predicate was built from multiple smaller predicates.
It simply sees:
Predicate<Product>7. Other Useful Predicate Combinations
Predicate provides three particularly useful composition methods:
and()
or()
negate()AND
isFood.and(isCheap)Means:
FOOD && CHEAPBoth conditions must be true.
OR
isFood.or(isCheap)Means:
FOOD || CHEAPAt least one condition must be true.
NOT
isFood.negate()Means:
!FOODIt reverses the predicate result.
These can also be chained:
isFood
.and(isCheap)
.and(isAvailable);Which reads naturally as:
FOOD
AND
CHEAP
AND
AVAILABLE8. Why Not Just Write One Big Lambda?
We could write:
Predicate<Product> predicate =
product ->
product.getCategory() == Category.FOOD &&
product.getPrice().compareTo(priceLimit) < 0;For two simple conditions, that’s perfectly reasonable.
Composition becomes more useful when conditions are:
- reused
- dynamically selected
- independently tested
- combined in different ways
For example:
isFood
isCheap
isAvailable
isInStock
isDiscountedWe can build different searches:
isFood AND isCheap
isFood AND isAvailable
isCheap AND isDiscounted
isAvailable AND NOT isDiscountedwithout rewriting the individual rules.
9. Practical Example: Dynamic Search Filters
Imagine an e-commerce search screen:
Category: FOOD
Max Price: €10
Available: YES
Discounted: ANYInstead of writing a separate search method for every combination, we can build predicates dynamically.
Conceptually:
User Search Criteria
│
▼
┌───────────────────────┐
│ isFood │
│ AND │
│ isCheap │
│ AND │
│ isAvailable │
└───────────────────────┘
│
▼
Predicate<Product>
│
▼
Search / Filter ProductsThis is where functional composition becomes particularly powerful.
Small rules become building blocks for larger business rules.
10. Function Composition vs Predicate Composition
The easiest way to distinguish them:
Think:
Function
↓
TRANSFORM → TRANSFORM → TRANSFORM
----------------------------------------------------------------------
Predicate
↓
CONDITION + CONDITION + CONDITION
The Bigger Picture
Functional composition encourages us to build programs from small pieces of behavior.
Small Function
+
Small Function
+
Small Function
│
▼
COMPOSE
│
▼
Larger BehaviorFor transformations:
Product
↓
Price
↓
Formatted Price
↓
MessageFor conditions:
Product
↓
Food?
+
Cheap?
+
Available?
↓
Combined PredicateThis is one of the central ideas behind functional programming:
Build complex behavior by composing small, focused, reusable functions.
Key Takeaways
- Functional composition means combining functions to create new functions.
Function.andThen()executes the current function first, followed by the next function.Function.compose()executes the supplied function first.Predicate.and()combines conditions using logical AND.Predicate.or()combines conditions using logical OR.Predicate.negate()reverses a condition.- Small functions are easier to reuse, test, and combine.
- Composition is especially useful for dynamic rules such as search filters and business criteria.
Remember This
Transformation?
Product → Price → String
↓
Function.andThen()
--------------------------------------------------------------
Multiple conditions?
Food AND Cheap AND Available
↓
Predicate.and()In short: Don’t always build one large function. Build small pieces of behavior and compose them like building blocks.
Continue Learning Functional Interfaces🚀
← Previous: Practical Examples of Standard Functional Interfaces | Next →: Coming Soon









