Context
A Predicate can be used to simplify complex nested if statements in Java. It embraces functional programming, making your code more readable and maintainable by encouraging you to define conditions with meaningful names rather than relying on a sequence of technical expressions.
A good practice is to use one predicate for one condition, and to combine them with other predicates to achieve your business goal.
At first, it may seem that writing multiple small predicates takes more time than writing a single big one. But let me share a real enterprise experience. The developer works on multiple Java components. Each time the client asks for a feature or a bugfix, a small portion of code is added to the codebase. Over time, the developer notices he’s rewriting code that already exists, with just a small condition that differs. So the the code is duplicated by 80% and only 20% is the actual feature requested.
He can’t reuse the existing code because verifying that it does exactly what’s needed would take too much time, time not allocated in the feature estimation. Moreover, he is young and doesn’t like the old-style syntax of the current code.
The estimation didn’t account for using existing code or doing refactoring. So he decides to write the feature from scratch in a better, more modern way.
Even sonar or IA tools, at the time of writing this article, aren’t capable of detecting this kind of duplication. And so on, generation after generation, the same mistakes are repeated, due to a lack of discipline, and good practices.
Senior developers keep saying that refactoring is necessary, but asking the client for a dedicated ticket is risky, because refactoring costs time, and time costs money. The client doesn’t see immediate value in the refactoring, so he’s less inclined to approve that extra cost, just to improve developer satisfaction 😅.
Bad Practices Example: Without Predicate Chaining 😪

public boolean isValidPerson(Person person, String nickname, String nicknamePrefix, String streetAddress) {
if (person != null) {
if (person.getNicknames() != null) {
boolean hasExactNickname = person.getNicknames().stream()
.anyMatch(n -> n.equalsIgnoreCase(nickname));
boolean hasNicknamePrefix = person.getNicknames().stream()
.anyMatch(n -> n.startsWith(nicknamePrefix));
if (hasExactNickname && hasNicknamePrefix) {
Address address = person.getAddress();
if (address != null && streetAddress.equalsIgnoreCase(address.getStreet())) {
return true;
}
}
}
}
return false;
}
Example Java Predicates and Functions
Predicates :
public class AddressPredicates {
public static Predicate<Address> hasStreetCode(@NonNull String streetCode) {
return address -> nonNullAddress().and(equalsIgnoreCaseStreetCode(streetCode)).test(address);
}
public static Predicate<Address> hasStreet(@NonNull String street) {
return address -> nonNullAddress().and(equalsIgnoreCaseStreet(street)).test(address);
}
public static Predicate<Address> equalsIgnoreCaseStreet(@NonNull String street) {
return address -> street.equalsIgnoreCase(address.getStreet());
}
public static Predicate<Address> equalsIgnoreCaseStreetCode(@NonNull String streetCode) {
return address -> streetCode.equalsIgnoreCase(address.getStreetCode());
}
public static Predicate<Address> nonNullAddress() {
return Objects::nonNull;
}
}
public class PersonPredicates {
public static Predicate<Person> hasNicknameAndHasNicknamePrefixAndAsStreetAddress(@NonNull String nickname,
@NonNull String nicknamePrefix,
@NonNull String streetAddress) {
return person -> hasNonNullPersonAndHasNickname(nickname)
.and(hasNonNullPersonAndHasNicknameStartWith(nicknamePrefix))
.and(hasNonNullPersonAndHasStreetAddress(streetAddress))
.test(person);
}
public static Predicate<Person> hasNonNullPersonAndHasNicknameStartWith(@NonNull String nicknamePrefix) {
return person -> nonNullPerson().and(hasNicknameStartWith(nicknamePrefix)).test(person);
}
public static Predicate<Person> hasNonNullPersonAndHasNickname(@NonNull String nickname) {
return person -> nonNullPerson().and(equalsIgnoreCaseNickname(nickname)).test(person);
}
public static Predicate<Person> hasNonNullPersonAndHasStreetAddress(@NonNull String streetAddress) {
return person -> nonNullPerson().and(hasStreetAddress(streetAddress)).test(person);
}
public static Predicate<Person> hasNonNullPersonAndHasStreetCodeAddress(@NonNull String streetCodeAddress) {
return person -> nonNullPerson().and(hasStreetCodeAddress(streetCodeAddress)).test(person);
}
public static Predicate<Person> equalsIgnoreCaseNickname(@NonNull String nickname) {
return person -> person.getNicknames().stream().anyMatch(equalsIgnoreCaseString(nickname));
}
public static Predicate<Person> hasNicknameStartWith(@NonNull String nicknamePrefix) {
return person -> person.getNicknames().stream().anyMatch(startWithString(nicknamePrefix));
}
public static Predicate<Person> hasStreetAddress(@NonNull String streetAddress) {
return person -> AddressPredicates.hasStreet(streetAddress).test(person.getAddress());
}
public static Predicate<Person> hasStreetCodeAddress(@NonNull String streetCodeAddress) {
return person -> AddressPredicates.hasStreetCode(streetCodeAddress).test(person.getAddress());
}
public static Predicate<Person> nonNullPerson() {
return Objects::nonNull;
}
}
public class StringPredicates {
public static Predicate<String> equalsIgnoreCaseString(@NonNull String value) {
return value::equalsIgnoreCase;
}
public static Predicate<String> startWithString(@NonNull String value) {
return str -> str.startsWith(value);
}
}
DTO :
public class Address {
private String streetCode;
private String street;
}
public class Person {
private String firstname;
private String lastname;
private Address address;
private List<String> nicknames;
}
Unit Test :
public class AddressPredicatesUnitTest {
@Test
public void AddressPredicates_hasStreet_returnTrue(){
// GIVEN
var address = Address.builder().street("Ferdinand").build();
// WHEN
var result = AddressPredicates.hasStreet("Ferdinand").test(address);
// THEN
assertTrue(result);
}
@Test
public void AddressPredicates_hasStreetCode_returnTrue(){
// GIVEN
var address = Address.builder().streetCode("22").build();
// WHEN
var result = AddressPredicates.hasStreetCode("22").test(address);
// THEN
assertTrue(result);
}
}
public class PersonPredicatesUnitTest {
@Test
public void PersonPredicates_hasNicknameAndHasNicknamePrefixAndAsStreetAddress_returnTrue() {
// GIVEN
var nicknames = List.of("el patron", "pablo");
var address = Address.builder().street("forest").build();
var person = Person.builder().nicknames(nicknames).address(address).build();
// WHEN
var result = PersonPredicates.hasNicknameAndHasNicknamePrefixAndAsStreetAddress("pablo", "el", "forest").test(person);
// THEN
assertTrue(result);
}
@Test
public void PersonPredicates_hasNonNullPersonAndHasNicknameStartWith_returnTrue() {
// GIVEN
var person = Person.builder().nicknames(List.of("el patron", "pablo1")).build();
// WHEN
var result = PersonPredicates.hasNonNullPersonAndHasNicknameStartWith("el").test(person);
// THEN
assertTrue(result);
}
@Test
public void PersonPredicates_equalsIgnoreCaseNickname_returnTrue() {
// GIVEN
var person = Person.builder().nicknames(List.of("el patron", "pablo1")).build();
// WHEN
var result = PersonPredicates.equalsIgnoreCaseNickname("pablo1").test(person);
// THEN
assertTrue(result);
}
@Test
public void PersonPredicates_hasNonNullPersonAndHasNickname_returnTrue() {
// GIVEN
var person = Person.builder().nicknames(List.of("el patron", "pablo")).build();
// WHEN
var result = PersonPredicates.hasNonNullPersonAndHasNickname("pablo").test(person);
// THEN
assertTrue(result);
}
@Test
public void PersonPredicates_hasNonNullPersonAndHasStreetCodeAddress_returnTrue() {
// GIVEN
var address = Address.builder().streetCode("222").build();
var person = Person.builder().address(address).build();
// WHEN
var result = PersonPredicates.hasNonNullPersonAndHasStreetCodeAddress("222").test(person);
// THEN
assertTrue(result);
}
@Test
public void PersonPredicates_hasNonNullPersonAndHasStreetAddress_returnTrue() {
// GIVEN
var address = Address.builder().street("Ferdinand2").build();
var person = Person.builder().address(address).build();
// WHEN
var result = PersonPredicates.hasNonNullPersonAndHasStreetAddress("Ferdinand2").test(person);
// THEN
assertTrue(result);
}
@Test
public void PersonPredicates_hasStreetAddress_returnTrue() {
// GIVEN
var address = Address.builder().street("Ferdinand").build();
var person = Person.builder().address(address).build();
// WHEN
var result = PersonPredicates.hasStreetAddress("Ferdinand").test(person);
// THEN
assertTrue(result);
}
@Test
public void PersonPredicates_hasStreetCodeAddress_returnTrue() {
// GIVEN
var address = Address.builder().streetCode("45").build();
var person = Person.builder().address(address).build();
// WHEN
var result = PersonPredicates.hasStreetCodeAddress("45").test(person);
// THEN
assertTrue(result);
}
}
Ressources
Github sources :