javajava-11functional-interfaceunary-operator

Mapping an object using a List<UnaryOperator<...>>


I have a List<UnaryOperator<String>> and need to map/transform a String by passing it through the list of operators. I have the following functional Java 11 code:

UnaryOperator<String> addA = (startString) -> startString + "a";
UnaryOperator<String> addB = (startString) -> startString + "b";
UnaryOperator<String> addc = (startString) -> startString + "c";
List<UnaryOperator<String>> operators = List.of(addA, addB, addc);
String concatenatedString =
    operators
        .stream()
        .reduce(
            "", // identity
            (value, op) -> op.apply(value), // accumulator
            (value1, value2) -> value1.concat(value2) // combiner
        );
System.out.println(concatenatedString); // prints "abc" as expected.

The concern I have is the string concatenation is expressed in 2 places. First in each of the UnaryOperators and second in the combiner argument. Makes me wonder if there is a better way to do this?


Solution

  • You can use the advantage that UnaryUperator<T> extends Function<T, T> and chain multiple calls of Function::andThen to get a composed UnaryOperator<String> of all within the list:

    UnaryOperator<String> mergedUnaryOperators = operators.stream()
                .reduce((l, r) -> (string) -> l.andThen(r).apply(string))
                .orElseGet(UnaryOperator::identity);
    
    String output = mergedUnaryOperators.apply("");      // results in "abc"
    

    To have a clearer picture how does it work, this is called inside the reduce method:

    new BinaryOperator<UnaryOperator<String>>() {
      @Override
      public UnaryOperator<String> apply(UnaryOperator<String> l, UnaryOperator<String> r) {
         return string -> l.andThen(r).apply(string);
      }
    }