En Java 8, puede usar una referencia de método para filtrar una secuencia, por ejemplo:
Stream<String> s = ...;
long emptyStrings = s.filter(String::isEmpty).count();
¿Hay alguna manera de crear una referencia de método que sea la negación de una existente, es decir, algo como:
long nonEmptyStrings = s.filter(not(String::isEmpty)).count();
Podría crear el not
método como a continuación, pero me preguntaba si el JDK ofrecía algo similar.
static <T> Predicate<T> not(Predicate<T> p) { return o -> !p.test(o); }
Predicate.not(Predicate)
método estático . Pero ese problema aún está abierto, así que lo veremos lo antes posible en Java 12 (si es que lo ha hecho).Respuestas:
Predicate.not( … )
java-11ofrece un nuevo método Predicate # not
Entonces puede negar la referencia del método:
fuente
Estoy planeando importar estáticamente lo siguiente para permitir que la referencia del método se use en línea:
p.ej
Actualización : a partir de Java-11, el JDK también ofrece una solución similar incorporada.
fuente
Hay una manera de componer una referencia de método que es lo contrario de una referencia de método actual. Vea la respuesta de @ vlasec a continuación que muestra cómo al
Predicate
convertir explícitamente la referencia del método a a y luego convertirla utilizando lanegate
función. Esa es una forma entre algunas otras formas no demasiado problemáticas de hacerlo.Lo contrario de esto:
Es esto:
o esto:
Personalmente, prefiero la técnica posterior porque me resulta más claro de leer
it -> !it.isEmpty()
que un reparto explícito largo y detallado y luego negarlo.También se podría hacer un predicado y reutilizarlo:
O, si tiene una colección o matriz, simplemente use un bucle for que sea simple, tenga menos sobrecarga y * podría ser ** más rápido:
* Si desea saber qué es más rápido, utilice JMH http://openjdk.java.net/projects/code-tools/jmh y evite el código de referencia manual a menos que evite todas las optimizaciones de JVM; consulte Java 8: rendimiento de Streams vs Colecciones
** Estoy recibiendo críticas por sugerir que la técnica for-loop es más rápida. Elimina la creación de una secuencia, elimina el uso de otro método de llamada (función negativa para predicado) y elimina una lista / contador de acumulador temporal. Entonces, algunas cosas que se guardan en la última construcción pueden hacerlo más rápido.
Sin embargo, creo que es más simple y agradable, incluso si no es más rápido. Si el trabajo requiere un martillo y un clavo, ¡no traiga una motosierra y pegamento! Sé que algunos de ustedes están en desacuerdo con eso.
lista de deseos: Me gustaría ver que las
Stream
funciones de Java evolucionen un poco ahora que los usuarios de Java están más familiarizados con ellas. Por ejemplo, el método 'contar' en Stream podría aceptar aPredicate
para que esto se pueda hacer directamente así:fuente
Predicate.negate(String::isEmpty);
sin el engorroso casting.Predicate
tiene métodosand
,or
ynegate
.Sin embargo,
String::isEmpty
no es unaPredicate
, es solo unaString -> Boolean
lambda y aún podría convertirse en cualquier cosa, por ejemploFunction<String, Boolean>
. La inferencia de tipos es lo que debe suceder primero. Elfilter
método infiere tipo implícitamente . Pero si lo niegas antes de pasarlo como argumento, ya no sucede. Como @axtavt mencionó, la inferencia explícita se puede usar como una forma fea:Hay otras formas recomendadas en otras respuestas, siendo el
not
método estático y el lambda las mejores ideas. Esto concluye la sección tl; dr .Sin embargo, si desea una comprensión más profunda de la inferencia de tipo lambda, me gustaría explicarlo un poco más en profundidad, usando ejemplos. Mire estos e intente descubrir qué sucede:
Predicate
aObject
- tonto pero válidaPredicate
aFunction
, ya no se trata de inferenciatest
definido por su lambdaInteger
no tieneisEmpty
métodoString::isEmpty
método estático conInteger
argumentoEspero que esto ayude a obtener más información sobre cómo funciona la inferencia de tipos.
fuente
Aprovechando las respuestas y la experiencia personal de otros:
fuente
::
referencia funcional como se desearía (String::isEmpty.negate()
), pero si asigna a una variable primero (o se convierte aPredicate<String>
primera), eso funciona. Creo que lambda w /!
será más legible en la mayoría de los casos, pero es útil saber qué se puede y qué no se puede compilar.Otra opción es utilizar la conversión lambda en contextos no ambiguos en una clase:
... y luego estática importar la clase de utilidad:
fuente
¿No debería
Predicate#negate
ser lo que estás buscando?fuente
Predicate
primero.String::isEmpty()
dePredicate<String>
antes - es muy feo.Predicate<String> p = (Predicate<String>) String::isEmpty;
yp.negate()
.s -> !s.isEmpty()
en ese caso!En este caso, podría usar el
org.apache.commons.lang3.StringUtils
y hacerfuente
String::isEmpty
como ejemplo. Todavía es información relevante si tiene este caso de uso, pero si solo responde al caso de uso de String, entonces no debería aceptarse.He escrito una clase de utilidad completa (inspirada en la propuesta de Askar) que puede tomar la expresión lambda de Java 8 y convertirla (si corresponde) en cualquier lambda Java 8 estándar escrita en el paquete
java.util.function
. Por ejemplo, puedes hacer:asPredicate(String::isEmpty).negate()
asBiPredicate(String::equals).negate()
Debido a que habría numerosas ambigüedades si todos los métodos estáticos se nombraran solo
as()
, opté por llamar al método "como" seguido del tipo devuelto. Esto nos da el control total de la interpretación lambda. A continuación se muestra la primera parte de la clase de utilidad (algo grande) que revela el patrón utilizado.Echa un vistazo a la clase completa aquí (en esencia).
fuente
Puede usar predicados de colecciones de Eclipse
Si no puede cambiar las cadenas de
List
:Si solo necesita una negación
String.isEmpty()
, también puede usarloStringPredicates.notEmpty()
.Nota: Soy colaborador de Eclipse Collections.
fuente
Puedes lograr esto siempre
emptyStrings = s.filter(s->!s.isEmpty()).count();
fuente
Si está utilizando Spring Boot (2.0.0+) puede usar:
Que hace:
return (str != null && !str.isEmpty());
Por lo tanto, tendrá el efecto de negación requerido para
isEmpty
fuente