JDK-8050818 : Predicate::not - provide an easier way to negate a predicate
  • Type: Enhancement
  • Component: core-libs
  • Sub-Component: java.util
  • Affected Version: 8u5
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2014-07-15
  • Updated: 2023-10-16
  • Resolved: 2018-05-30
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 11
11 b17Fixed
Related Reports
CSR :  
Duplicate :  
Relates :  
Relates :  
Description
A DESCRIPTION OF THE REQUEST :
Please add a method to java.util.Predicate similar to the following:

     public static <T> Predicate<T> not(Predicate<T> target) {
          return target.negate();
     }

JUSTIFICATION :
There is currently no easy way to negate a predicate which is a lambda (as opposed to an actual Predicate object).

Suppose I want to create a list of empty strings using the Stream API. It is easy:

List<String> result = originals.stream().filter(String::isEmpty).collect(Collections.toList());

However, if I instead want to create a list of nonempty strings, I have to do a bit more work. Suddenly, I am forced to introduce a named variable and do an explicit method call:

List<String> result = originals.stream().filter(string -> !string.isEmpty()).collect(Collections.toList());

I can't use a method reference here because there is no method in the String class for determining if a string is not empty. (I wish there were!)

In principle, the Predicate.negate() method is intended to be used in these sorts of situations, but in this case it can't be, because String::isEmpty is not a Predicate<String> until and unless it is assigned to one, so I would have to create a temporary Predicate object (or use a cast) to get a Predicate to call negate() on.

I would like to be able to easily create a negated version of a lambda, like this:

import static java.util.function.Predicate.not;

List<String> result = originals.stream().filter(not(String::isEmpty)).collect(Collections.toList());

Also see discussion here:

http://stackoverflow.com/questions/21488056/how-to-negate-a-method-reference-predicate



EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
I would like to be able to write this:

import static java.util.function.Predicate.not;

List<String> result = originals.stream().filter(not(String::isEmpty)).collect(Collections.toList());

or for slightly better performance (assuming the JDK's optimizer fails at noticing that 'not(String::isEmpty)' is a constant expression - I'm not sure if it can handle this), I could write this:

Predicate<String> notEmpty = not(String::isEmpty);

List<String> result = originals.stream().filter(notEmpty).collect(Collections.toList());

ACTUAL -
I currently have to write this:

List<String> result = originals.stream().filter(string -> !string.isEmpty()).collect(Collections.toList());

or this:

List<String> result = originals.stream().filter(string -> ((Predicate)String::isEmpty).negate()).collect(Collections.toList());

or this:

Predicate<String> isEmpty = String::isEmpty;
List<String> result = originals.stream().filter(isEmpty.negate()).collect(Collections.toList());

all of which are harder to read and unnecessarily different from the equivalent code which uses a direct method reference to the String::isEmpty method.


Comments
URL: http://hg.openjdk.java.net/jdk/jdk/rev/5a9acf84c34a User: jlaskey Date: 2018-05-30 15:45:33 +0000
30-05-2018

Previously this was "add static java.util.Predicate.not(Predicate) method". While this seems sensible, this isn't obviously the right way to go. The main issue is that to negate a predicate, what you need already has to be a predicate. Unfortunately a lambda or correctly-shaped method reference isn't a Predicate until a target type is provided, and in some cases no target type is available. So that's the real problem: no target type is available in some cases, and so the solution should be to figure out how to provide a target type in those cases. Another issue is that a single static method not() doesn't sit well with the existing methods on Predicate, which are default methods and(), or(), and negate(). It's possible to add static methods and(p1, p2) and or(p1, p2) and this doesn't cause any ambiguity with direct calls to the default methods of the same name, as their arities are different. However, because of argument shifting, adding such static methods *does* cause problems with method references. Thus, Predicate::and and Predicate::or would become ambiguous. This is source-incompatible. One way to establish a target type is to add a static method such as <T> Predicate.of(Predicate<T>) that simply returns its argument. That would improve call sites somewhat. An alternative might be to name the method predicate() which would make a static import more palatable. Consider: 1) filter(((Predicate<String>)String::isEmpty).negate()) 2) filter(Predicate.of(String::isEmpty).negate()) 3) filter(predicate(String::isEmpty).negate()) 4) filter(not(String::isEmpty)) 1) current 2) with Predicate.of 3) with Predicate.predicate, statically imported 4) with Predicate.not, statically imported
06-04-2018