Existe-t-il un moyen en Java8 d'utiliser une référence de méthode en tant qu'objet Function
pour utiliser ses méthodes, quelque chose comme:
Stream.of("ciao", "hola", "hello")
.map(String::length.andThen(n -> n * 2))
Cette question n'est pas liée au Stream
, elle est utilisée comme exemple, j'aimerais avoir une réponse sur la référence de la méthode
Vous pouvez écrire une méthode statique pour ce faire:
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);
}
}
Vous pouvez ensuite le placer dans une classe utilitaire et l'importer statiquement.
Alternativement, créez une méthode statique plus simple qui juste retourne la fonction qui lui est donnée, pour que le compilateur sache ce que vous faites:
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;
}
}
Vous pouvez simplement l'enregistrer dans une variable:
Function<String, Integer> toLength = String::length;
Stream.of("ciao", "hola", "hello")
.map(toLength.andThen(n -> n * 2));
Ou vous pouvez utiliser un casting, mais c'est moins lisible, IMO:
Stream.of("ciao", "hola", "hello")
.map(((Function<String, Integer>) String::length).andThen(n -> n * 2));
Vous devriez pouvoir réaliser ce que vous voulez en ligne en utilisant des transtypages:
Stream.of("ciao", "hola", "hello")
.map(((Function<String, Integer>) String::length).andThen(n -> n * 2))
Il n'y a que des "indices de type" pour le compilateur, donc ils ne "castent" pas réellement l'objet et n'ont pas la surcharge d'un cast réel.
Alternativement, vous pouvez utiliser une variable locale pour la lisibilité:
Function<String, Integer> fun = String::length
Stream.of("ciao", "hola", "hello")
.map(fun.andThen(n -> n * 2));
Une troisième façon qui peut être plus concise est d'utiliser une méthode utilitaire:
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));
Veuillez noter que ceci n'est pas testé, donc je ne sais pas si l'inférence de type fonctionne correctement dans ce cas.
Vous pouvez également utiliser
Function.identity().andThen(String::length).andThen(n -> n * 2)
Le problème est, String::length
n'est pas nécessairement un Function
; il peut se conformer à de nombreuses interfaces fonctionnelles. Il doit être utilisé dans un contexte qui fournit le type cible, et le contexte peut être - affectation, invocation de méthode, transtypage.
Si Function
pouvait fournir une méthode statique uniquement pour le typage cible, nous pourrions faire
Function.by(String::length).andThen(n->n*2)
static <T, R> Function<T, R> by(Function<T, R> f){ return f; }
Par exemple, j'utilise cette technique dans ne interface fonctionnelle
static <T> AsyncIterator<T> by(AsyncIterator<T> asyncIterator)
Sucre de syntaxe pour créer un AsyncIterator à partir d'une expression lambda ou d'une référence de méthode.
Cette méthode renvoie simplement l'argument
asyncIterator
, ce qui semble un peu étrange. Explication:AsyncIterator étant une interface fonctionnelle, une instance peut être créée par une expression lambda ou une référence de méthode, dans 3 contextes:
// Assignment Context
AsyncIterator<ByteBuffer> asyncIter = source::read;
asyncIter.forEach(...);
// Casting Context
((AsyncIterator<ByteBuffer>)source::read)
.forEach(...);
// Invocation Context
AsyncIterator.by(source::read)
.forEach(...);
La 3ème option est meilleure que les deux autres, et c'est le but de cette méthode.
Vous pouvez utiliser un casting
Stream.of("ciao", "hola", "hello")
.map(((Function<String, Integer>) String::length)
.andThen(n -> n * 2))
.forEach(System.out::println);
impressions
8
8
10
Vous pourriez écrire:
Stream.of("ciao", "hola", "hello").map(String::length).map(n -> n * 2);
Nous pouvons utiliser la fonction reduce
pour combiner plusieurs fonctions en une seule.
public static void main(String[] args) {
List<Function<String, String>> normalizers = Arrays.asList(
str -> {
System.out.println(str);
return str;
},
String::toLowerCase,
str -> {
System.out.println(str);
return str;
},
String::trim,
str -> {
System.out.println(str);
return str;
});
String input = " Hello World ";
normalizers.stream()
.reduce(Function.identity(), (a, b) -> a.andThen(b))
.apply(input);
}
Production:
Hello World
hello world
hello world