0

Suppose I have a method which is something like

void getCalled(Predicate<Integer> predicate, List<Integer> lst){
   lst.stream().filter(predicate).forEach(...);
}

The thing is, for this predicate I will have exactly two choices, which are negations of each other. So the predicates are:

public class Helper {
   public static boolean doesExist(int x){
    return ..
   }

   public static boolean doesNotExist(int x){
     return !doesExist();
   }
}

Now in the caller I have confusion, which is, should I extract this

void caller(){
  List<Integer> lst = ...
  getCalled(lst, Helper::doesExist);
  getCalled(lst, Helper::doesNotExist);
}

The issue, I am not sure having extra code for Helper::doesNotExist is a good design practice since its just calling the negation of doesExist, something like:

void caller(){
  List<Integer> lst = ...
  getCalled(lst, Helper::doesExist);
  getCalled(lst, x -> !Helper.doesExist(x)); //Ugly
}

At the same time, this is the cleanest way to submit a predicate to the getCalled method, by having two different predicates, doesExist and doesNotExist.

Any idea

  • The JDK in places includes methods which are simple negations of each other, for example Objects::isNull and Objects::nonNull (whose javadoc suggests they are intended for use as Predicates). The JDK isn't perfect but this could be one point in favour of that approach. – just me Feb 2 at 16:10
2

Java streams can be collected into so called partitions

Map<Boolean, List<Integer>> partitions = 
        lst.stream().collect(Collectors.partitioningBy(Helper::doesExist));

The Map now contains two lists, one list of the "existing" elements with the key true:

partitions.get(true).forEach(...);

and a list with the "non-existing" with the key false:

partitions.get(false).forEach(...);
|improve this answer|||||
1

It's reasonable to ask whether a method that simply negates another should be written, because we often want to avoid duplicating logic. In your example though, the two are so simple that I wouldn't worry about their implementations (and therefore behavior) drifting apart in the future.

Having separate methods Helper::doesExist and Helper::doesNotExist is good practice in my opinion, because it's easy to read and therefore reason about.

Another option if you're running JDK11 is to use Predicate.not.

void caller(){
  List<Integer> lst = ...
  getCalled(lst, Helper::doesExist);
  getCalled(lst, not(Helper::doesExist));
}

I still think that separate methods are easier to read.

|improve this answer|||||

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.