tutorial funciones funcional expresiones ejemplos ejemplo java lambda functional-programming predicate java-8

funciones - stream filter java 7



¿Por qué el predicado<T> de Java 8 no extiende la función<T, Boolean> (4)

El método en Predicate<T> devuelve boolean . El método en Function<T, Boolean> devuelve Boolean . Ellos no son los mismos. Aunque hay autoboxing, los métodos Java no usan clases de contenedor cuando lo hacen las primitivas. Además, existen diferencias como Boolean puede ser null mientras boolean no.

Es aún más diferente en el caso de Consumer<T> . El método en Consumer<T> tiene el tipo de retorno void , lo que significa que puede devolver o devolver implícitamente utilizando return; , pero el método en Function<T, Void> debe regresar usando return null; explícitamente.

Si escribí la interfaz Predicate, me gustaría codificar en la interfaz el hecho de que es solo una función que devuelve un booleano primitivo, como este:

@FunctionalInterface public interface Predicate<T> extends Function<T, Boolean> { boolean test(T t); @Override default Boolean apply(T t) { return Boolean.valueOf(test(t)); } }

Me preguntaba, ¿hay alguna razón convincente para que los diseñadores de la API de Java 8 elijan mantener el predicado completamente separado de la función? ¿Hay alguna evidencia de que consideraron hacerlo y decidieron no hacerlo? Supongo que una pregunta similar se aplica a todas las demás interfaces funcionales ''especiales'' como Consumer (podría ser Function <T, Void>), Supplier (Function <Void, T>) y funciones primitivas como IntFunction (Function <Integer, T>).

No he pensado muy profundamente sobre todas las ramificaciones de esto, así que probablemente me esté perdiendo algo.

EDITAR: Algunas de las respuestas resaltan la distinción semántica entre aplicar y probar. No digo que no aprecie la distinción, y estoy de acuerdo en que es beneficioso tener esta distinción. Lo que no entiendo es por qué un predicado, sin embargo, no es también una función de la misma manera que, por ejemplo, una lista es una colección o el doble es un número, que es un objeto.

Si Predicate (y todas las demás interfaces funcionales genéricas especiales, como Consumer, Supplier, IntUnaryOperator, etc.) tuvieran esta relación con Function, permitiría usarla en el lugar donde se espera el parámetro Function (lo que nos viene a la mente es composición con otras funciones, por ejemplo llamar a myFunction.compose (myPredicate) o evitar escribir varias funciones especializadas en una API cuando dicha implementación de auto (un) boxing como la descrita anteriormente sea suficiente)

EDIT 2: Viendo el proyecto openjdk lambda, encontré que las interfaces funcionales primitivas utilizadas para extender la función hasta este compromiso de Brian Goetz el 2012-12-19 . No pude encontrar razones específicas para el cambio en ninguna de las listas de correo del grupo de expertos de JDR o lambda-dev en ese momento.


En mi opinión, la Function<T, R> es solo la definición de una función genérica. Si todos los FunctionalInterfaces implementaran Function , el único método abstracto debería llamarse apply() . En el contexto de un FunctionalInterface concreto como FilterFile , el método abstracto boolean accept(File pathname) es un nombre mucho mejor que Boolean apply(File) .

La anotación @FunctionalInterface ya marca una interfaz que pretende ser utilizable como FunctionalInterface . No hay beneficio si todos implementan una interfaz base que no sea otra que procesarla de manera genérica. No veo cuándo no te interesaría la semántica de una FunctionalInterface por adelantado para que estén disponibles para llamar y apply para todas ellas.


No es una respuesta directa a su pregunta, pero ¿para qué la usaría?

Tenga en cuenta la situación siguiente: desea asignar verdadero / falso a la lista de valores para los que es verdadero, respectivamente, falso.

Con tu código puedes usar:

@FunctionalInterface interface CustomPredicate<T> extends Function<T, Boolean> { boolean test(T value); @Override default Boolean apply(T t) { return test(t); } }

List<String> stringList = new ArrayList<>(); stringList.add("a"); stringList.add("hg"); stringList.add("dsl"); stringList.add("sldi"); stringList.add("ilsdo"); stringList.add("jlieio"); CustomPredicate<String> customPredicate = str -> (str.length() >= 3); Map<Boolean, List<String>> mapping = stringList.stream() .collect(Collectors.groupingBy(customPredicate));

Sin embargo, lo siguiente me dice que definitivamente han pensado en algo similar, ya que ofrecen el método de partición:

List<String> stringList = new ArrayList<>(); stringList.add("a"); stringList.add("hg"); stringList.add("dsl"); stringList.add("sldi"); stringList.add("ilsdo"); stringList.add("jlieio"); Predicate<String> predicate = str -> (str.length() >= 3); Map<Boolean, List<String>> mapping = stringList.stream() .collect(Collectors.partitioningBy(predicate));

Algunas razones por las que puedo pensar son:

  • No sería intuitivo tener un método apply() disponible en un Predicate en el que solo se espera un método de test() .
  • Las interfaces funcionales están diseñadas para proporcionar solo un tipo de funcionalidad básica (excluyendo el encadenamiento o las operaciones lógicas), en su situación CustomPredicate contiene dos tipos de funcionalidad. Solo aumentaría la confusión.

No hay necesidad de una jerarquía de herencia sospechosa. Estas interfaces funcionales son intercambiables.

Function<A,Boolean> f1=…; Predicate<A> p1=…; Predicate<A> p2=f1::apply; Function<A,Boolean> f2=p1::test;

Esto funciona en ambas direcciones. Entonces, ¿por qué debería haber una relación de herencia anunciando una dirección específica?