Reducing a list of UnaryOperators in Java 8

It's possible to convert a functional interface to another functional interface by means of using a method reference syntax on its abstract method.

import java.util.function.UnaryOperator;
import java.util.stream.Stream;

public class Example {
    public static void main(String[] args) {
        Stream<UnaryOperator<String>> stringMappers = Stream.of(
                s -> s + "bar",
                s -> "[" + s + "]",
                s -> s + s
        );
        UnaryOperator<String> f = stringMappers.reduce(
                s -> s,
                (a, b) -> a.andThen(b)::apply
        );
        System.out.println(f.apply("foo"));
    }
}

The problem with using compose or andThen is that they're built into the Function interface and the type -- both compile-time and runtime types -- of the functions they return is Function and not UnaryOperator or a subinterface such as you've defined. For example, suppose we have

UnaryOperator<String> a = s -> s + "bar";
UnaryOperator<String> b = s -> s + s;

One might think we could write

UnaryOperator<String> c = a.compose(b);

but this doesn't work! Instead, one has to write

Function<String, String> c = a.compose(b);

For this to work, UnaryOperator would have to provide covariant overrides of andThen and compose. (Arguably this is a bug in the API.) You'd do the same in your subinterface. Or, it's simple enough to write out the lambdas by hand. For example,

interface MyOperator extends UnaryOperator<String> { }

public static void main(String[] args) {
    List<MyOperator> list =
        Arrays.asList(s -> s + "bar",
                      s -> "[" + s + "]",
                      s -> s + s);

    MyOperator composite =
        list.stream()
            .reduce(s -> s, (a, b) -> s -> b.apply(a.apply(s)));

    System.out.println(composite.apply("foo"));
}

This prints out [foobar][foobar]. Note that I've used the two-arg form of reduce in order to avoid having to deal with Optional.

Alternatively, if you're doing function composition a lot, you could reimplement the methods you need in your own interface. It's not too hard. These are based on the implementations in java.util.Function but with the concrete String type I've been using in this example substituted for the generics.

interface MyOperator extends UnaryOperator<String> {
    static MyOperator identity() {
        return s -> s;
    }

    default MyOperator andThen(MyOperator after) {
        Objects.requireNonNull(after);
        return s -> after.apply(this.apply(s));
    }

    default MyOperator compose(MyOperator before) {
        Objects.requireNonNull(before);
        return s -> this.apply(before.apply(s));
    }
}

This would be used as follows:

MyOperator composite =
    list.stream()
        .reduce(MyOperator.identity(), (a, b) -> a.andThen(b));

Whether bulking up the interface in order to write andThen instead of a nested lambda is a matter of taste, I guess.


MyFilter inherits the method andThen from Function and therefore the returned type is Function and cannot be cast to MyFilter. But since it has the desired function signature, you can create the MyFilter instance using a lambda or method reference.

E.g. change (f1,f2)->(MyFilter)f1.andThen(f2) to (f1,f2)-> f1.andThen(f2)::apply.

With this change the entire method looks like:

public static MyObject filterIt(List<MyFilter> filters, MyObject obj) {
    Optional<MyFilter> mf =
      filters.stream().reduce( (f1,f2)-> f1.andThen(f2)::apply);
    return mf.map(f->f.apply(obj)).orElse(obj);
}

But you should rethink your design. There is no need to have the resulting function to be an instance of MyFilter, in fact, even the input can be relaxed to accept more than just List<MyFilter>:

// will accept List<MyFilter> as input
public static MyObject filterIt(
 List<? extends Function<MyObject,MyObject>> filters, MyObject obj) {
   List<Function<MyObject,MyObject>> l=Collections.unmodifiableList(filters);
   Optional<Function<MyObject,MyObject>> mf=l.stream().reduce(Function::andThen);
   return mf.orElse(Function.identity()).apply(obj);
}

or, using Stuart Marks’ hint for getting rid of the Optional:

// will accept List<MyFilter> as input
public static MyObject filterIt(
  List<? extends Function<MyObject,MyObject>> filters,MyObject obj) {
    List<Function<MyObject,MyObject>> l=Collections.unmodifiableList(filters);
    return l.stream().reduce(Function.identity(), Function::andThen).apply(obj);
}

Just for completeness, alternatively you could chain your MyFilters on a stream rather than composing a new function:

public static MyObject filterIt2(List<MyFilter> filters,MyObject obj) {
    Stream<MyObject> s=Stream.of(obj);
    for(MyFilter f: filters) s=s.map(f);
    return s.findAny().get();
}