W3docs

Interface Predicate do Java

Teste condições sobre valores em Java com a interface funcional Predicate e seus combinadores and/or/negate.

Predicate<T> é a interface funcional para a pergunta "este valor é bom?" — uma entrada do tipo T, uma resposta boolean. Ela está no coração de Stream.filter, Collection.removeIf, Optional.filter e de todo método do JDK que diz "mantenha os que correspondem." A interface é pequena — um único método test(T) — mas vem com uma pequena álgebra de combinadores (and, or, negate, isEqual, not) que permite construir condições complexas a partir de condições simples, sem jamais escrever a lógica booleana manualmente.

Este capítulo tem a mesma estrutura dos demais capítulos detalhados de interfaces da Parte 12: a interface, seus três ou quatro métodos úteis, a álgebra e depois um exemplo prático.

A interface

A declaração completa, em paráfrase:

@FunctionalInterface
public interface Predicate<T> {
  boolean test(T t);                         // the only abstract method

  default Predicate<T> and(Predicate<? super T> other);
  default Predicate<T> or(Predicate<? super T> other);
  default Predicate<T> negate();
  static  <T> Predicate<T> isEqual(Object target);
  static  <T> Predicate<T> not(Predicate<? super T> target);   // Java 11+
}

test é o único método abstrato que lambdas e referências de métodos implementam. Todo o resto é construído sobre ele. Você raramente chamará test diretamente — stream().filter(...) e list.removeIf(...) o chamam por você — mas conhecer o nome do método importa quando você escreve código que aceita um Predicate<T> e precisa invocá-lo.

Predicate<String> notBlank = s -> !s.isBlank();
boolean ok = notBlank.test("hello");          // true

and, or, negate — álgebra booleana sem a cola

Os três métodos default compõem predicados da mesma forma que os operadores &&, ||, ! compõem booleanos:

Predicate<String> notNull  = Objects::nonNull;
Predicate<String> notBlank = s -> !s.isBlank();
Predicate<String> longEnough = s -> s.length() >= 3;

Predicate<String> useful   = notNull.and(notBlank).and(longEnough);
Predicate<String> usableOrShort = useful.or(s -> s.length() == 1);
Predicate<String> bad      = useful.negate();

Duas propriedades são importantes:

  • Curto-circuito, na ordem de declaração. a.and(b) só chama b.test quando a.test retornou true. a.or(b) só chama b.test quando a.test retornou false. Esta é a mesma ordem de avaliação de && e ||, o que significa que você pode colocar verificações baratas e que falham frequentemente primeiro, e as custosas por último.
  • Cada chamada retorna um novo Predicate. Os combinadores não alteram o this. Reutilize os originais à vontade.

negate() simplesmente inverte o resultado. useful.negate() retorna true para nulos, strings em branco e strings menores que 3 — todo caso que useful rejeitou.

Predicate.not — a negação legível

O Java 11 adicionou um atalho estático:

list.removeIf(Predicate.not(String::isBlank));    // remove every blank string

Predicate.not(p) produz a mesma resposta boolean que p.negate(), mas compõe muito mais naturalmente no ponto de chamada. A forma de referência de método String::isBlank é um Predicate<String> por si só — mas você não pode escrever (String::isBlank).negate(), porque o compilador precisa de um tipo alvo antes de resolver a referência. Predicate.not(String::isBlank) fornece esse tipo alvo, e o resultado todo se lê como "not blank" em ordem natural.

Um import estático de Predicate.not torna as cadeias de filtros ainda mais limpas:

import static java.util.function.Predicate.not;
...
var nonBlank = lines.stream().filter(not(String::isBlank)).toList();

Predicate.isEqual — igualdade segura para null

Predicate<Object> isFoo = Predicate.isEqual("foo");        // o -> Objects.equals(o, "foo")

A implementação é literalmente t -> Objects.equals(target, t), o que significa que null em qualquer lado é comparado com segurança. Raramente economiza teclas em relação a s -> s.equals("foo"), mas de fato salva quando o stream pode conter nullnull.equals("foo") lançaria NPE, enquanto Objects.equals(null, "foo") retorna false.

Onde Predicate<T> aparece no JDK

O mesmo Predicate<T> flui por todas as APIs de "filtro":

Stream<String> kept = stream.filter(notBlank);             // Stream.filter
boolean removed     = list.removeIf(String::isBlank);      // Collection.removeIf
Optional<String> ok = opt.filter(notBlank);                // Optional.filter
boolean any         = stream.anyMatch(notBlank);           // anyMatch / allMatch / noneMatch
map.values().removeIf(String::isBlank);                    // Map view + Collection.removeIf

Todos eles têm a mesma forma, então um Predicate<T> construído uma vez é reutilizável em todas as direções — e montá-lo com and/or/negate é exatamente como se evita o cheiro de "tenho três filtros ligeiramente diferentes, todos quase duplicados".

Especializações primitivas — IntPredicate, LongPredicate, DoublePredicate

Predicate<Integer> funciona com ints, mas cada chamada encapsula a entrada. Para pipelines numéricas intensas, o pacote fornece:

IntPredicate    even = n -> n % 2 == 0;
LongPredicate   big  = n -> n > 1_000_000_000L;
DoublePredicate hot  = d -> d > 37.5;

A mesma álgebra and/or/negate, sem boxing. Estas são as que IntStream.filter aceita — usar Predicate<Integer> ali forçaria o stream a fazer autoboxing de cada elemento na entrada.

BiPredicate<T, U> — testes com dois argumentos

Quando a pergunta requer duas entradas (uma chave e um valor, uma linha e uma coluna, um antigo e um novo), use BiPredicate:

BiPredicate<String, Integer> longEnoughFor = (s, n) -> s.length() >= n;
boolean ok = longEnoughFor.test("hello", 4);             // true

A superfície de combinadores é menor — and, or, negate existem, mas não há isEqual ou not para dois argumentos. Map.removeIf((k, v) -> ...) é exatamente um BiPredicate<K, V>.

Um exemplo prático: predicados, composição, a álgebra e onde se encaixam

O programa abaixo constrói três predicados simples sobre User, compõe-os com and/or/negate, demonstra o curto-circuito contando chamadas, substitui Predicate.not por negação em um ponto de chamada removeIf e usa um IntPredicate com um IntStream para mostrar a variante primitiva.

java— editable, runs on the server

O que extrair da execução:

  • Os três predicados base (adult, active, namedWell) permaneceram reutilizáveis. eligible, minor e reachable foram construídos por composição, em vez de escrever três lambdas separadas com lógica sobreposta.
  • and fez curto-circuito exatamente como &&: expensive rodou menos vezes que cheap porque cada menor foi rejeitado antes da verificação custosa ser acionada. Essa é a alavanca que você tem para ordenar — coloque verificações baratas e que falham frequentemente primeiro.
  • Predicate.not(...) no ponto de chamada removeIf foi lido como inglês simples ("remove if not non-blank") e evitou precisar de um tipo alvo antes da negação. Importar estaticamente not é o toque final.
  • Predicate.isEqual("foo") contou as duas entradas "foo" passando por um null sem lançar exceção. s -> s.equals("foo") teria lançado NPE no elemento null.
  • IntPredicate even = n -> n % 2 == 0; encaixou diretamente em IntStream.filter sem boxing — e o mesmo combinador .and(...) funciona na especialização primitiva.

O que vem a seguir

Predicate<T> responde sim ou não. O próximo capítulo, Interface Function do Java, aborda a interface para a outra metade do trabalho com streams: transformar um valor em outro. A forma — método único, composição com métodos default (andThen, compose, mais o estático identity()) — é a mesma de Predicate, e as mesmas lições sobre ordenação, reutilização e especializações primitivas se aplicam.

Prática

Prática
Você tem `Predicate<String> notNull = Objects::nonNull;` e `Predicate<String> notBlank = s -> !s.isBlank();` e quer um predicado que retorne `true` apenas quando a string for não-nula e não-vazia, com `notNull` verificado *primeiro* para que `notBlank` nunca rode sobre um `null`. Qual expressão faz isso corretamente?
Você tem `Predicate<String> notNull = Objects::nonNull;` e `Predicate<String> notBlank = s -> !s.isBlank();` e quer um predicado que retorne `true` apenas quando a string for não-nula e não-vazia, com `notNull` verificado *primeiro* para que `notBlank` nunca rode sobre um `null`. Qual expressão faz isso corretamente?
Was this page helpful?