fdelsert
fdelsert

Reputation: 808

How to suitably compose Predicate and Function in a Java function?

The purpose is to create a new Predicate usable in a stream filter :

myCollectionOfElement
.stream()
.filter(
    MyStaticHelperClass.compose(MyStaticHelperClass.getSubElement1OfTheElement(),MyStaticHelperClass.getPredicate1OnSubElement1()))
.sorted(MyStaticHelperClass.getOtherSubElement().reversed())
.limit(10)
.collect(Collectors.toList())

getSubElement1OfTheElement() returns Function<E,S> (E contains a S property) getPredicate1OnSubElement1() returns Predicate<S>

I use static functions to expose method references and functions. I do this because the stream is called in a Velocity template and this context doesn't support lambda syntax and method reference. I don't want to create a static function for all possible combinaisons, so I really want them to be composable.

For example here, I don't want to have a static getPredicate1OnElementThatCheckProperty1OnTheSubElement1() because I can compose getSubElement1OfTheElement() and getPredicate1OnSubElement1().

So I need a compose function :

// returns a new Predicate constructed by applying Predicate predicate on the result of Function function
public static <E,S> Predicate<E> compose(Function<E,S> function, Predicate<S> predicate)

// most intuitive : lambda
return value -> predicate.test(function.apply(value));

// with method references
return function.andThen(predicate::test)::apply;
// predicate.compose is not available because Predicate interface doesn't extends Function interface

inspired by Is there a convenience method to create a Predicate that tests if a field equals a given value?

// step by step with variables
Function <S,Boolean> predicateFunction = predicate::test;
// a kind of @FunctionalInterface implicit "conversion" ? Predicate -> Function.
// is that safe ?

Function <E,Boolean> composed = function.andThen(predicateFunction::apply);
return composed::apply;

Edit :

It's called a cast context : https://docs.oracle.com/javase/8/docs/api/java/util/function/package-summary.html

// the implementation of my compose can also be this ugly one-liner :
return ((Function <S,Boolean>)predicate::test).compose(function)::apply;

So, we cannot implement a generic compose function taking any functional interface (in my case Function and Predicate) because the name of the abstract method differs for each interface (test and apply in my case).
I'm OK with that.

To conclude, what I really need is two static functions, one that converts a Predicate to a Function and the opposite. Every Predicate will be used as a Function and the final operation will convert the composed Function to Predicate in order to match with the parameter type of the filter function.

public static <S> Function<S,Boolean> predicateToFunction(Predicate<S> predicate){
    return predicate::test;
}
public static <S> Predicate<S> functionToPredicate(Function<S,Boolean> function){
    return function::apply;
}

Is that correct ?
If so, is there any interest in releasing the bounds in the function signature ?

Upvotes: 12

Views: 9912

Answers (3)

ahrooran
ahrooran

Reputation: 1115

Adding to @fdelsert, here is my implementation of a composition predicate.

I was in the process of removing guava from a project for some reasons.

Guava uses its own version of Predicate and Function to create a Composed Predicate which now is obsolete since Java integrated those into standard function library. I wrote a similar class that follows same signature as Guava but uses POJ classes instead.

Might be useful for future users:

public class CompositionPredicate<A, B>
        implements Predicate<A>, Serializable {

    private final Predicate<? super B> predicate;

    private final Function<A, ? extends B> function;

    public CompositionPredicate(Predicate<B> predicate, Function<A, ? extends B> function) {
        Objects.requireNonNull(predicate);
        Objects.requireNonNull(function);
        this.predicate = predicate;
        this.function = function;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof CompositionPredicate<?, ?>) {
            @SuppressWarnings("PatternVariableCanBeUsed") CompositionPredicate<?, ?> that = (CompositionPredicate<?, ?>) obj;
            return function.equals(that.function) && predicate.equals(that.predicate);
        }
        return false;
    }

    @Override
    public int hashCode() {
        return function.hashCode() ^ predicate.hashCode();
    }

    @Override
    public String toString() {
        return predicate + "(" + function + ")";
    }

    @Override
    public boolean test(A a) {
        return predicate.test(function.apply(a));
    }
}

Upvotes: 0

Mike Reardon
Mike Reardon

Reputation: 135

I think the best approach would be use the boolean compose methods Predicate provides: and, or, not. e.g.,

private Predicate<String> startsWith(String prefix) {
    return s -> s.startsWith(prefix);
}

private Predicate<String> endsWith(String suffix) {
    return s -> s.endsWith(suffix);
}

Stream.of("Foo","Fuz","Doo","Fo")
    .filter(startsWith("F").and(endsWith("o")))
    .forEach(System.out::println);

Upvotes: 0

fdelsert
fdelsert

Reputation: 808

I answer my own questions.

Use lambda :

value -> predicate.test(function.apply(value));

Or if you really want/have to write a compose function, signature must be something like :

public static <E,S> Predicate<E> compose(Function<E,S> function, Predicate<? super S> predicate)

Upvotes: 8

Related Questions