Como negar um predicado de referência do método

No Java 8, você pode usar uma referência de método para filtrar um stream, por exemplo:

Stream s = ...; long emptyStrings = s.filter(String::isEmpty).count(); 

Existe uma maneira de criar uma referência de método que é a negação de um existente, ou seja, algo como:

 long nonEmptyStrings = s.filter(not(String::isEmpty)).count(); 

Eu poderia criar o método not como abaixo, mas eu queria saber se o JDK oferecia algo semelhante.

 static  Predicate not(Predicate p) { return o -> !p.test(o); } 

java-11 oferece um novo método Predicado # não

Então você pode negar a referência do método:

 Stream s = ...; long nonEmptyStrings = s.filter(Predicate.not(String::isEmpty)).count(); 

Estou planejando importar estático o seguinte para permitir que a referência do método seja usada inline:

 public static  Predicate not(Predicate t) { return t.negate(); } 

por exemplo

 Stream s = ...; long nonEmptyStrings = s.filter(not(String::isEmpty)).count(); 

Atualização : – Bem, o JDK / 11 pode estar oferecendo uma solução semelhante também.

Existe uma maneira de compor uma referência de método que é o oposto de uma referência de método atual. Veja a resposta de @vlasec abaixo que mostra como explicitamente converter a referência de método para um Predicate e depois convertê-lo usando a function negate . Esse é um caminho entre algumas outras maneiras não problemáticas de fazê-lo.

O oposto disso:

 Stream s = ...; int emptyStrings = s.filter(String::isEmpty).count(); 

é isto:

 Stream s = ...; int notEmptyStrings = s.filter(((Predicate) String::isEmpty).negate()).count() 

ou isto:

 Stream s = ...; int notEmptyStrings = s.filter( it -> !it.isEmpty() ).count(); 

Pessoalmente, eu prefiro a técnica posterior porque eu acho mais claro lê- it -> !it.isEmpty() que um longo e explícito casting e depois nega.

Pode-se também fazer um predicado e reutilizá-lo:

 Predicate notEmpty = (String it) -> !it.isEmpty(); Stream s = ...; int notEmptyStrings = s.filter(notEmpty).count(); 

Ou, se tiver uma coleção ou matriz, use um loop for que seja simples, tenha menos sobrecarga e * seja ** mais rápido:

 int notEmpty = 0; for(String s : list) if(!s.isEmpty()) notEmpty++; 

* Se você quiser saber o que é mais rápido, use o JMH http://openjdk.java.net/projects/code-tools/jmh e evite o código de benchmark manual, a menos que ele evite todas as otimizações da JVM – consulte Java 8: performance do Streams vs collections

** Estou recebendo críticas por sugerir que a técnica for-loop é mais rápida. Ele elimina uma criação de stream, elimina o uso de outra chamada de método (function negativa para predicado) e elimina uma lista / contador de acumulador temporário. Então, algumas coisas são salvas pela última construção que pode torná-lo mais rápido.

Eu acho que é mais simples e mais agradável, mesmo que não seja mais rápido. Se o trabalho exigir um martelo e um prego, não traga uma motosserra e cola! Eu sei que alguns de vocês têm problema com isso.

wish-list: Eu gostaria de ver as funções do Java Stream evoluírem um pouco agora que os usuários Java estão mais familiarizados com elas. Por exemplo, o método ‘count’ no Stream pode aceitar um Predicate para que isso possa ser feito diretamente assim:

 Stream s = ...; int notEmptyStrings = s.count(it -> !it.isEmpty()); or List list = ...; int notEmptyStrings = lists.count(it -> !it.isEmpty()); 

Predicate tem methods and , or e negate .

No entanto, String::isEmpty não é um Predicate , é apenas um String -> Boolean lambda String -> Boolean e ainda pode se tornar qualquer coisa, por exemplo, Function . A inferência de tipos é o que precisa acontecer primeiro. O método de filter infere o tipo implicitamente . Mas se você negar isso antes de passá-lo como argumento, isso não acontece mais. Como @axtavt mencionou, a inferência explícita pode ser usada de uma maneira feia:

  s.filter(((Predicate) String::isEmpty).negate()).count() 

Existem outras maneiras aconselhadas em outras respostas, com o método static e lambda sendo as melhores ideias. Isto conclui a seção dr .


No entanto, se você quiser um entendimento mais profundo da inferência do tipo lambda, eu gostaria de explicar um pouco mais a profundidade, usando exemplos. Olhe para estes e tente descobrir o que acontece:

  Object obj1 = String::isEmpty; Predicate p1 = s -> s.isEmpty(); Function f1 = String::isEmpty; Object obj2 = p1; Function f2 = (Function) obj2; Function f3 = p1::test; Predicate p2 = s -> s.isEmpty(); Predicate p3 = String::isEmpty; 
  • obj1 não compila – os lambdas precisam inferir uma interface funcional (= com um método abstrato)
  • p1 e f1 funcionam bem, cada um inferindo um tipo diferente
  • obj2 lança um Predicate para o Object – bobo, mas válido
  • f2 falha em tempo de execução – você não pode converter Predicate em Function , não é mais sobre inferência
  • f3 funciona – você chama o test método do predicado que é definido pelo seu lambda
  • p2 não compila – Integer não tem método isEmpty
  • p3 também não compila – não há nenhum método estático String::isEmpty com o argumento Integer

Espero que isso ajude a entender melhor como o tipo de inferência funciona.

Com base nas respostas e experiências pessoais de outros:

 Predicate blank = String::isEmpty; content.stream() .filter(blank.negate()) 

Outra opção é utilizar o casting lambda em contextos não ambíguos em uma única class:

 public static class Lambdas { public static  Predicate as(Predicate predicate){ return predicate; } public static  Consumer as(Consumer consumer){ return consumer; } public static  Supplier as(Supplier supplier){ return supplier; } public static  Function as(Function function){ return function; } } 

… e, em seguida, estático, importe a class do utilitário:

 stream.filter(as(String::isEmpty).negate()) 

Não deve Predicate#negate ser o que você está procurando?

Nesse caso, você poderia usar o org.apache.commons.lang3.StringUtils e fazer

 int nonEmptyStrings = s.filter(StringUtils::isNotEmpty).count(); 

Você pode usar predicados de collections do Eclipse

 MutableList strings = Lists.mutable.empty(); int nonEmptyStrings = strings.count(Predicates.not(String::isEmpty)); 

Se você não pode mudar as cordas da List :

 List strings = new ArrayList<>(); int nonEmptyStrings = ListAdapter.adapt(strings).count(Predicates.not(String::isEmpty)); 

Se você só precisa de uma negação de String.isEmpty() você também pode usar StringPredicates.notEmpty() .

Nota: Eu sou um colaborador do Eclipse Collections.

Eu escrevi uma class de utilitário completa (inspirada na proposta de Askar) que pode pegar a expressão lambda do Java 8 e transformá-las (se aplicável) em qualquer padrão lambda Java 8 padrão definido no pacote java.util.function . Você pode, por exemplo, fazer:

  • asPredicate(String::isEmpty).negate()
  • asBiPredicate(String::equals).negate()

Como haveria várias ambiguidades se todos os methods estáticos fossem nomeados apenas as() , optei por chamar o método “as” seguido pelo tipo retornado. Isso nos dá controle total da interpretação lambda. Abaixo está a primeira parte da class de utilitários (um tanto grande) revelando o padrão usado.

Dê uma olhada na turma completa aqui (em essência).

 public class FunctionCastUtil { public static  BiConsumer asBiConsumer(BiConsumer biConsumer) { return biConsumer; } public static  BiFunction asBiFunction(BiFunction biFunction) { return biFunction; } public static  BinaryOperator asBinaryOperator(BinaryOperator binaryOperator) { return binaryOperator; } ... and so on... }