Shortening the list of UnaryOperators in Java 8

What is the preferred way to shorten the list of UnaryOperators in Java 8 until they constitute one UnaryOperator that I can call? For example, I have the following

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

But this code raises a ClassCastException at (MyFilter)f1.andThen(f2) . I really want to get the effect of this code at the end:

 MyObject o = obj; for(MyFilter f:filters){ o = f.apply(o); } return o; 

But I'm also interested in how we can reduce a set of functions to one function using compose or andThen .

+7
java java-8 functional-programming
source share
3 answers

The problem with using compose or andThen is that they are built into the Function interface, and the type — both the compilation time and the execution types — of the returned Function functions and not the UnaryOperator or subsequence that you defined. For example, suppose that

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

You might think that we could write

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

but it will not work! Instead, write

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

For this to work, UnaryOperator would have to provide covariant overrides of andThen and compose . (Perhaps this is a bug in the API.) You would do the same in your subinterface. Or, it's simple enough to write lambdas manually. 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")); } 

Prints [foobar][foobar] . Note that I used a form with two reduce arguments so as not to deal with Optional .

Alternatively, if you do a lot of composition, you can override the methods you need in your own interface. This is not too complicated. They are based on implementations in java.util.Function , but with the specific String type that I used in this example, they replaced 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 will be used as follows:

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

I think that increasing the scope of the interface for writing andThen instead of the nested lambda is a matter of taste.

+11
source share

MyFilter inherits the andThen method from Function , and therefore the return type is Function and cannot be passed to MyFilter . But since it has the required function signature, you can instantiate MyFilter using a lambda or method reference.

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

With this change, the whole method looks like this:

 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 have to rethink your design. There is no need to have the resulting function as an instance of MyFilter , in fact even the input can be weakened 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 the Stuart Marks hint to get rid of 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, otherwise you can associate your MyFilter with a stream, rather than create 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(); } 
+2
source share

Short answer: never use the UnaryOperator<T> interface, use Function<T, T> . If you do, the methods will compose andThen will work as expected.

+1
source share

All Articles