JoeG
JoeG

Reputation: 7652

Java 8, how to compose (Bi)Function from (Bi)Predicate

I don't believe this question is specific to the Bi versions of these Java 8 classes, hence the parens in the question title.

I composed a function to create an Apache Commons Lang3 Pair object with the following requirements:

  1. If both Left and Right are null, then the Pair should be null
  2. If Left and Right are equal to one another, then the Pair should be null
  3. Otherwise, a Pair should be created, either side of which may be null

I did this within a method as follows:

BiPredicate<String,String> valuesExist = (pre, post) -> pre != null || post != null;

final BiPredicate<String, String> valuesDiffer = valuesExist.and((pre, post) -> 
            (pre != null) ? ! pre.equals(post) : true);

BiFunction<String, String, Pair<String,String>> createPair = 
            (pre, post) -> (valuesDiffer.test(pre, post)) ?
                            ImmutablePair.of(pre, post) : null;  

// usage looks like:
Pair<String, String> myPair = createPair.apply(myValue1, myValue2)              

I had 3 questions:

  1. Is there a way to avoid the 2 BiPredicates? The second one is only needed because referencing inside the BiFunction requires it to be final
  2. Is there a way to move this out of the method and into the surrounding class?
  3. Is there obvious "clean-up" I missed?

Update

Based upon one of the answers below, a solution without any Java-8 features also seems practical:

static boolean filterPairArgs(String pre, String post) {
    return (pre != null || post != null) && ((pre == null) || !pre.equals(post));
}

static Pair<String, String> createPair(String v1, String v2) {
    if (filterPairArgs(v1, v2)) {
       return new Pair<>(v1, v2);
    } 
    return null;
}

// usage looks like:
Pair<String, String> myPair = createPair(myValue1, myValue2);

Upvotes: 1

Views: 1406

Answers (2)

erickson
erickson

Reputation: 269807

The null-safe handling of Objects.equals() method will clean things up here.

Personally, I'd put this in a helper function. You can use it functionally as a tidy method reference or simply call it as a method.

static Pair<String, String> toPair(String pre, String post) {
  return Objects.equals(pre, post) ? null : ImmutablePair.of(pre, post);
}

Upvotes: 3

pedromss
pedromss

Reputation: 2453

You can join the predicates:

BiPredicate<String,String> valuesExist = (pre, post) -> (pre != null || post != null) && ((pre == null) || !pre.equals(post));
BiFunction<String, String, Pair<String,String>> createPair =
        (pre, post) -> (valuesExist.test(pre, post)) ?
                new Pair<>(pre, post) : null;

Personally I would create 2 methods/functions instead:

boolean filterPairArgs(String pre, String post) {
    return (pre != null || post != null) && ((pre == null) || !pre.equals(post));
}

Pair<String, String> createPair(String v1, String v2, BiPredicate<String, String> pred) {
    if(pred.test(v1, v2)) {
        return new Pair<>(v1, v2);
    } 
    return null;
}

// usage looks like:
Pair<String, String> myPair = createPair(myValue1, myValue2, this::filterPairArgs);

Upvotes: 0

Related Questions