Search

Dark theme | Light theme

February 19, 2021

Java Joy: Composing Functions

In Java we can write single argument functions that implement the java.util.function.Function interface. We can combine multiple functions into a new function using the andThen and compose methods from the Function interface. We need to give another function as argument to these methods. When we use the andThen method the output of the original function will be input of the function passed as argument. With the compose method our function will get as input the output of the function that is passed as argument. It is important to know the difference, because it can change the result of the function we are composing. The andThen and compose methods are also available on the IntUnaryOperator, LongUnaryOperator and DoubleUnaryOperator interface.

In the following example we use both andThen and compose to chain together some functions. We can see the result can be different when using andThen and compose with the same functions.

package com.mrhaki.sample;

import java.util.Map;
import java.util.function.Function;
import java.util.function.IntUnaryOperator;
import java.util.function.UnaryOperator;

public class Compose {
    public static void main(String[] args) {
        // Two simple functions that take a int argument
        // and do some calculations.
        IntUnaryOperator f = x -> 11 + (x - 90);
        IntUnaryOperator g = x -> x * 2;

        // Using andThen will first execute f and use
        // the result as input for g:
        // (11 + (100 - 90)) * 2
        assert 42 == f.andThen(g).applyAsInt(100);

        // Using compose will first execute g and use
        // the result as input for f:
        // 11 + ((100 * 2) - 90)
        assert 121 == f.compose(g).applyAsInt(100);


        // Map with some user data.
        var user =
                Map.of("name", "Hubert",
                       "alias", "MrHaki");

        // Function to duplicate a String.
        UnaryOperator<String> duplicate = s -> String.format("%1$s,%1$s", s);

        // Function to turn String into lowercase.
        UnaryOperator<String> lowerCase = String::toLowerCase;

        // Function with Map parameter to create a new function with
        // a String parameter that will get the
        // value for a given key from the map that is passed.
        Function<Map<String, String>, UnaryOperator<String>> getFromMap =
                map -> key -> map.get(key);

        // Chain using andThen.
        Function<String, String> andThenUserKey =
                getFromMap.apply(user)
                          .andThen(lowerCase)
                          .andThen(duplicate);

        assert "mrhaki,mrhaki".equals(andThenUserKey.apply("alias"));

        // Chain using compose.
        Function<String, String> composeUserKey =
                duplicate.compose(lowerCase)
                         .compose(getFromMap.apply(user));

        assert "mrhaki,mrhaki".equals(composeUserKey.apply("alias"));
    }
}

Written with Java 15.