6

In isReadyToDeliver method if all products in order is available (ProductState.AVAILABLE) and if order state is ready to send (OrderState.READY_TO_SEND), method must return true. I wrote both two part but I couldn't combine them in return phrase,

I wrote return orderState.andThen(productState) but get this error:

The method andThen(Function<? super Boolean,? extends V>) in the type Function<Order,Boolean> is not applicable for the arguments (Function<Order,Boolean>)

public class OrderFunctions  {

    public Function<Order, Boolean> isReadyToDeliver() {            
        Function<Order, Boolean> orderState = o -> o.getState() == OrderState.READY_TO_SEND;            
        Function<Order, Boolean>  productState = 
                o -> o.getProducts()
                    .stream()
                    .map(Product -> Product.getState())
                    .allMatch(Product -> Product == ProductState.AVAILABLE);

        return ????? ; 
       //return  orderState.andThen(productState);
       //error: The method andThen(Function<? super Boolean,? extends V>) in the type Function<Order,Boolean> is not applicable for the arguments (Function<Order,Boolean>)      
    }
}

In case other classes are needed:

enum OrderState {CONFIRMED, PAID, WAREHOUSE_PROCESSED, READY_TO_SEND, DELIVERED }

enum ProductType { NORMAL, BREAKABLE, PERISHABLE }

public class Order {

    private OrderState state;
    private List<Product> products = new ArrayList<>();

    public OrderState getState() {
        return state;
    }

    public void setState(OrderState state) {
        this.state = state;
    }

    public Order state(OrderState state) {
        this.state = state;
        return this;
    }

    public List<Product> getProducts() {
        return products;
    }

    public void setProducts(List<Product> products) {
        this.products = products;
    }

    public Order product(Product product) {
        if (products == null) {
            products = new ArrayList<>();
        }
        products.add(product);
        return this;
    }
}

public class Product {

    private String code;
    private String title;
    private ProductState state;

    public ProductState getState() {
        return state;
    }

    public void setState(ProductState state) {
        this.state = state;
    }

    public Product state(ProductState state) {
        this.state = state;
        return this;
    }
}
pirho
  • 11,565
  • 12
  • 43
  • 70
Javad Akbari
  • 95
  • 1
  • 8
  • 1
    You're using Function where you should be using Predicate. https://docs.oracle.com/javase/8/docs/api/java/util/function/Predicate.html – JB Nizet Jan 20 '18 at 16:00
  • 2
    Minor note: please surround code text with `\`` rather than `**` to make it fixed-width and obviously code to readers. – Chai T. Rex Jan 20 '18 at 17:57

2 Answers2

6

If you change isReadyToDeliver() to return Predicate<Order> then you will be able to combine two predicates with .and(Predicate another) function:

public Predicate<Order> isReadyToDeliver() {
    Predicate<Order> orderState = o -> o.getState() == OrderState.READY_TO_SEND;

    Predicate<Order> productState =
                o -> o.getProducts()
                   .stream()
                   .map(Product -> Product.getState())
                   .allMatch(Product -> Product == ProductState.AVAILABLE);

    return orderState.and(productState);
}

Your example with functions composition didn't work, because when you compose functions f and g, g takes as a parameter value that f function returns. In your case it was broken, because orderState expected Order and return Boolean and it this case orderState.andThen() expected a function that takes Boolean as a parameter and returns something else. This requirement was not satisfied, because productState expected Order and returned Boolean. This is exactly what following error said:

error: The method andThen(Function) in the type Function is not applicable for the arguments (Function)

But if for some reason you want to stay with Function<Order, Boolean> then you will have return a lambda like:

public Function<Order, Boolean> isReadyToDeliver() {
    Function<Order, Boolean> orderState = o -> o.getState() == OrderState.READY_TO_SEND;

    Function<Order, Boolean> productState =
            o -> o.getProducts()
                    .stream()
                    .map(Product -> Product.getState())
                    .allMatch(Product -> Product == ProductState.AVAILABLE);


    return (order) -> orderState.apply(order) && productState.apply(order);
}
Szymon Stepniak
  • 40,216
  • 10
  • 104
  • 131
2

From the two functions orderState and productState you can create a new lambda expression by using && (the logical and) and return it:

public Function<Order, Boolean> isReadyToDeliver() {

    Function<Order, Boolean> orderState = ...;

    Function<Order, Boolean>  productState = ...;


    return o -> orderState.apply(o) && productState.apply(o);
} 
Thomas Fritsch
  • 9,639
  • 33
  • 37
  • 49