java - dominio - combinacion de funciones ejemplos
Referencia de método Java8 utilizada como objeto Function para combinar funciones (6)
¿Hay alguna manera en Java8 de utilizar una referencia de método como un objeto Function
para usar sus métodos, algo así como:
Stream.of("ciao", "hola", "hello")
.map(String::length.andThen(n -> n * 2))
Esta pregunta no está relacionada con el Stream
, se usa solo como ejemplo, me gustaría tener una respuesta sobre el método de referencia
Debería poder lograr lo que quiere en línea mediante el uso de moldes:
Stream.of("ciao", "hola", "hello")
.map(((Function<String, Integer>) String::length).andThen(n -> n * 2))
Solo hay ''sugerencias de tipo'' para el compilador, por lo que en realidad no ''lanzan'' el objeto y no tienen la sobrecarga de un molde real.
Alternativamente, puede usar una variable local para la legibilidad:
Function<String, Integer> fun = String::length
Stream.of("ciao", "hola", "hello")
.map(fun.andThen(n -> n * 2));
Una tercera forma que puede ser más concisa es con un método de utilidad:
public static <T, X, U> Function<T, U> chain(Function<T, X> fun1, Function<X, U> fun2)
{
return fun1.andThen(fun2);
}
Stream.of("ciao", "hola", "hello")
.map(chain(String::length, n -> n * 2));
Tenga en cuenta que esto no está probado, por lo tanto, no sé si la inferencia de tipo funciona correctamente en este caso.
Podrías escribir:
Stream.of("ciao", "hola", "hello").map(String::length).map(n -> n * 2);
Puedes escribir un método estático para hacer esto:
import java.util.function.*;
class Test {
public static void main(String[] args) {
Function<String, Integer> function = combine(String::length, n -> n * 2);
System.out.println(function.apply("foo"));
}
public static <T1, T2, T3> Function<T1, T3> combine(
Function<T1, T2> first,
Function<T2, T3> second) {
return first.andThen(second);
}
}
Luego puede ponerlo en una clase de utilidad e importarlo estáticamente.
Alternativamente, crea un método estático más simple que simplemente devuelve la función que se le da, por el bien del compilador que sabe lo que estás haciendo:
import java.util.function.*;
class Test {
public static void main(String[] args) {
Function<String, Integer> function = asFunction(String::length).andThen(n -> n * 2);
System.out.println(function.apply("foo"));
}
public static <T1, T2> Function<T1, T2> asFunction(Function<T1, T2> function) {
return function;
}
}
Puedes guardarlo en una variable:
Function<String, Integer> toLength = String::length;
Stream.of("ciao", "hola", "hello")
.map(toLength.andThen(n -> n * 2));
O puede usar un elenco, pero es menos legible, IMO:
Stream.of("ciao", "hola", "hello")
.map(((Function<String, Integer>) String::length).andThen(n -> n * 2));
Puedes usar un molde
Stream.of("ciao", "hola", "hello")
.map(((Function<String, Integer>) String::length)
.andThen(n -> n * 2))
.forEach(System.out::println);
huellas dactilares
8
8
10
También puedes usar
Function.identity().andThen(String::length).andThen(n -> n * 2)
El problema es que String::length
no es necesariamente una Function
; puede ajustarse a muchas interfaces funcionales. Debe usarse en un contexto que proporcione el tipo de objetivo, y el contexto podría ser: asignación, invocación de método, conversión.
Si la Function
puede proporcionar un método estático solo por el objetivo de escribir a máquina, podríamos hacer
Function.by(String::length).andThen(n->n*2)
static <T, R> Function<T, R> by(Function<T, R> f){ return f; }
Por ejemplo, uso esta técnica en una interfaz funcional
static <T> AsyncIterator<T> by(AsyncIterator<T> asyncIterator)
Azúcar de sintaxis para crear un AsyncIterator a partir de una expresión lambda o una referencia de método.
Este método simplemente devuelve el argumento
asyncIterator
, lo que parece un poco extraño. Explicación:Como AsyncIterator es una interfaz funcional, se puede crear una instancia mediante una expresión lambda o una referencia de método, en 3 contextos:
// Assignment Context
AsyncIterator<ByteBuffer> asyncIter = source::read;
asyncIter.forEach(...);
// Casting Context
((AsyncIterator<ByteBuffer>)source::read)
.forEach(...);
// Invocation Context
AsyncIterator.by(source::read)
.forEach(...);
La tercera opción se ve mejor que las otras dos, y ese es el propósito de este método.