2

I have the following situation, where the lambda expression i used to replace a working for loop does not work. Have no clue as to why this fails

public class Abc implements IAbc {

    // some fields
    ...
    // field i'm interested in
    @Inject @Any
    private Instance<HandlerInterface> handlers;

    // more members
    ...
    // method i'm interested in
    @Override
    public boolean hasHandler(List<Order> orders) {
        for (Order anOrder : orders) {
            for (HandlerInterface aHandler : handlers) {
                // following canHandler() is implemented by each 
                // handler that implements the HandlerInterface
                if(aHandler.canHandle(anOrder)) {
                    return true;
                }
            }
        return false;
    }
    // rest of the class content
    .....
}

So I was actually trying to replace the above code, within the method, with Lambdas (to which i'm new). The following was my replacement code

public boolean hasHandler(List<Order> orders) {
    return orders.stream().anyMatch(order ->
        Stream.of(handlers).map(Provider::get).anyMatch(handler ->
            handler.canHandle(order)));
}

The above lambda expression fails at handler.canHandle with AmbiguousResolutionException. I can't find out why it works with the for loop and not with stream. Im sure im doing something wrong here - but have no clue as to what. Any help on this is greatly appreciated.

2
  • what does Provider::get return ? Commented May 4, 2018 at 7:17
  • 2
    Why are you inserting a Provider::get step that doesn’t exist in the loop code? Commented May 4, 2018 at 8:07

2 Answers 2

2

I'm assuming Instance<HandlerInterface> implements Iterable<HandlerInterface> (or you wouldn't be able to use it in the enhanced for loop).

This means you can create a Stream of the elements of this Iterable by calling StreamSupport.stream(handlers.spliterator(), false);

So now, your method can be converted to use Streams as follows:

public boolean hasHandler(List<Order> orders) {
    return orders.stream()
                 .anyMatch(o -> StreamSupport.stream(handlers.spliterator(), false)
                                             .anyMatch(h -> h.canHandle(o)));
}

Note: I removed the .map(Provider::get) step, since it has no corresponding step in your original nested loop code.

Sign up to request clarification or add additional context in comments.

1 Comment

I tried this code out and it worked like magic. Thanks a lot.
0

In such cases create predicates starting from the innermost condition

First predicate is

if(aHandler.canHandle(anOrder)) {
  return true;
}

You can write this in lambda as

private static Predicate<? super HandlerInterface> p1(Order o) {
    return h -> h.canHandle(o);
}

Second(though is not very intuitive) but it is you want to check that for your order any handler that matches predicate p1, hence write that as

private static Predicate<? super Order> p2(List<HandlerInterface> handlers){
    return o -> handlers.stream().anyMatch(p1(o));
}

Hence complete solution becomes

orders.stream().anyMatch(p2(handlers));

When written inline and removing braces because of single-line predicates you can re-write this as

orders.stream().anyMatch(o -> handlers.stream().anyMatch(h -> h.canHandle(o)));

3 Comments

thanks for the response. The handlers.stream() would not work i think as it's not a Collection
it should be iterable for sure else we can't use it in advanced for loop
handlers is of type Instance here. It's not a collection. The iterable implementations are injected by CDI at runtime. Im new to using all these so im not sure, but this is my guess

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.