Skip to content

Java Predicates with distributive law

I have a boolean Java statement that is structured like follows:

return A AND (B OR C OR D)

In Java:

return A && (B || C || D);

I wanted to reformulate this by using chained predicates, but it seems like I have to use the distributive law of Boolean algebra in order to make it work:

(A AND B) OR (A AND C) OR (A AND D) which forces me to repeat A all the time.

This is what I ended up with in java:

return A.and(B)
        .or(A.and(C))
        .or(A.and(D))
        .test(params)

This seems not to lead to the same results, as my test cases fail when I’m using the predicate version.

Is this not possible with Java Predicates without making use of the distributive law or am I doing something wrong?

Answer

You can write your statement exactly as you want to:

A.and(B.or(C).or(D))

EDIT:

Here’s an example for your question from the comments:

// here you tell Java that p1 is of type Predicate
// though you can use "and" and "or"
Predicate<String> p1 = s -> s.equals("foo");
p1.and(s -> !s.equals("bar"));

But you could also create your own functional interface with the same signature but different default methods:

public interface MyCustomPredicate<T> {
    boolean test(T value);

    default MyCustomPredicate<T> and(MyCustomPredicate<? super T> other) {
        return (t) -> true;
    }
}

// ...

// here you tell Java that p2 is of type MyCustomPredicate
// though you can't use "or"
// and MyCustomPredicate.and does not behave the same as Predicate.and
MyCustomPredicate p2 = s -> s.equals("bar");
p2.and(s -> !s.equals("bar"));

It’s a bit different from pre-lambda Java. The signature of Predicate.and says that it accepts a Predicate. But in reality this means that it accepts any lambda that matches T -> boolean.

Predicate<String> p1 = s -> s.equals("foo");
MyCustomPredicate p2 = s -> s.equals("bar");
// this works because MyCustomPredicate has the same T -> boolean signature as Predicate
p1.or(p2);

You can cast lambdas inline as well:

Predicate<String> p3 = (s) -> true;
p3.and(((Predicate<String>) x -> true).and(x -> true));

Summarized: If you just write s -> s.equals("bar") Java doesn’t know if this lambda is a Predicate or any other functional interface that has the same signature T -> boolean.