Explain Codes LogoExplain Codes Logo

How to negate a method reference predicate

java
predicate-not
java-11
method-reference
Nikita BarsukovbyNikita Barsukov·Sep 18, 2024
TLDR

Negate a method reference in Java via a lambda that flips the predicate result:

Predicate<String> notEmpty = s -> !s.isEmpty();

This line gives you a Predicate that gives a true for non-empty strings, thus upturning the String::isEmpty method reference.

Java 11: Working the magic with Predicate.not

From Java 11 onwards, you have this nifty method, Predicate.not, that helps you achieve negation rather elegantly:

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

Here, Predicate.not paves an easy path for code simplification and boosts readability, particularly when dealing with streams:

long nonEmptyCount = strings.stream().filter(Predicate.not(String::isEmpty)).count();

This is equivalent to the more basic early approach but hits the ball out of the park by leveraging new language features.

For the Old School: Java 8 and Predicate.negate()

For those determined souls still riding the Java 8 lifeboat, while Predicate.not isn't available, you've still got a saviour in Predicate.negate(). Here's how:

Predicate<String> notEmpty = String::isEmpty; strings.stream().filter(notEmpty.negate()).count();

If JavaScript has taught us anything, it's that we can define our own functions. You can create your own 'not' utility method:

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

And then aim for plentiful tidiness by using static import in your code:

import static your.package.YourUtilClass.not; strings.stream().filter(not(String::isEmpty)).count();

Yes, you can thank me later!

Time to get Practical: Improving code readability and understanding nuances

Keep it simple, silly!

'Cause sometimes a simple for-loop can beat a complex stream operation when it comes to readability. Prioritize neat, maintainable code over showy language gymnastics:

List<String> nonEmptyStrings = new ArrayList<>(); for (String s : strings) { // You're in or you're out! if (!s.isEmpty()) { nonEmptyStrings.add(s); } }

Performance Showdown with JMH

If you're worried about speed, don't rely on your stopwatch. Use Java Microbenchmark Harness (JMH) for accurate performance comparisons. In shootouts, always rely on professional equipment!

Method References and the type inference puzzle

Type inference can be a tricky business with method references and lambda expressions, especially when generics enter the equation. Always ensure the compiler has enough breadcrumbs to figure out the types correctly.

Not all operations are created equal

In Java, Predicate is the Swiss Army knife when it comes to logical operations. You can chain multiple conditions to whip up more complex predicates:

Predicate<String> isNotEmptyAndLowerCase = Predicate.not(String::isEmpty) .and(s -> s.equals(s.toLowerCase()));

Casting Spells

There may be occasions where you need to explicitly swing your wand and cast to unravel type inference issues for the compiler:

// Without casting, the compiler might throw a tantrum here: Stream.of("a", "b", "").filter(((Predicate<String>) String::isEmpty).negate())... // Casting soothes the compiler nerves: Stream.of("a", "b", "").filter(((Predicate<String>) String::isEmpty).negate())...

Simple can be smarter

In a "Fast and Furious" scene where performance is the "Vin Diesel", opt for a less pretty but turbo-charged solution. But for most code, Betty White (a.k.a. readability and maintainability) should be your spirit animal.

Don't reinvent the wheel

Just like you don’t need to create your own java.lang.String, always keep an eye out for built-in language features like Predicate.not. They offer proven, efficient solutions and lessen the chance of a leaked abstract factory builder singleton proxy strategy decorator memento flyweight facade. But of course, who doesn’t love those?