Le terme interface fonctionnelle a fait son apparition dans Java 8. En Java, une interface fonctionnelle est une interface qui contient seulement une méthode abstraite (non implémentée). Une interface fonctionnelle peut comporter des méthodes par défaut et des méthodes statiques qui possèdent une implémentation, en plus de la méthode unique non implémentée.
Voici un exemple d’interface fonctionnelle Java :
public interface MonInterfaceFonctionnelle {
public void execute();
}
L’exemple ci-dessus est considéré comme étant une interface fonctionnelle en Java car elle ne contient qu’une seule méthode, et cette méthode n’a pas d’implémentation. Normalement les méthodes d’une interface Java ne contient pas d’implémentations, par contre ces méthodes par défaut peut contenir des implémentations, ou bien ces méthodes statiques. Vous trouverez ci-dessous un autre exemple d’interface fonctionnelle Java, avec des implémentations de certaines des méthodes :
public interface MonInterfaceFonctionnelle2{
public void execute();
public default void print(String text) {
System.out.println(text);
}
public static void print(String text, PrintWriter writer) throws IOException {
writer.write(text);
}
}
L’exemple ci-dessus est toujours considéré comme une interface fonctionnelle en Java, car il ne contient qu’une seule méthode non implémentée.
Les interfaces fonctionnelles peuvent être implémentées avec une expression lambda
Les interfaces fonctionnelles en Java peuvent être implémentées avec une expression Lambda Java. Voici un exemple qui implémente l’interface fonctionnelle MyFunctionalInterface définie au début de ce tutoriel sur l’interface fonctionnelle Java :
MonInterfaceFonctionnelle lambda = () -> {
System.out.println("Executing...");
}
Dans cet exemple l’expression lambda Java implémente la seule méthode/fonction abstraite de l’interface.
Interfaces fonctionnelles disponible en Java
Java contient un ensemble d’interfaces fonctionnelles conçues pour faire face aux cas d’utilisation les plus courants, ce qui évite de créer vos propres interfaces fonctionnelles pour chaque petit cas d’utilisation. Dans les sections suivantes, je décrirai certaines de ces interfaces fonctionnelles intégrées dans Java.
Function
L’interface Java Function (java.util.function.Function) est l’une des interfaces fonctionnelles les plus importantes de Java. L’interface Function représente une fonction (méthode) qui prend un seul paramètre et renvoie une seule valeur. Voici à quoi ressemble la définition de l’interface Function :
public interface Function<T,R> {
public <R> apply(T parameter);
}
L’interface Function contient en réalité quelques méthodes supplémentaires outre celle indiquée ci-dessus, mais comme elles sont toutes fournies avec une implémentation par défaut, vous n’avez pas besoin à implémenter ces méthodes supplémentaires.
La seule méthode que vous devez implémenter pour mettre en œuvre l’interface Function est la méthode apply(). Voici un exemple d’implémentation de la fonction :
public class AjouterTrois implements Function<Long, Long> {
@Override
public Long apply(Long unLong) {
return unLong + 3;
}
}
Cette implémentation de Function implémente la méthode apply() qui prend un Long comme paramètre, et retourne un Long. Voici un exemple d’utilisation de la classe AjouterTrois ci-dessus :
Function<Long, Long> ajouteur = new AjouterTrois ();
Long result = adder.apply((long) 4);
System.out.println("result = " + result);
Tout d’abord, cet exemple crée une nouvelle instance AjouterTrois et l’affecte à une variable de type Function. Ensuite, l’exemple appelle la méthode apply() de l’instance de AjouterTrois. Troisièmement, l’exemple imprime le résultat (qui est 7).
Vous pouvez également implémenter l’interface Function en utilisant une expression lambda Java. Voici à quoi cela ressemble :
Function<Long, Long> ajouteur = (value) -> value + 3;
Long resultaLambda = adder.apply((long) 8);
System.out.println("resultLambda = " + resultLambda);
Comme vous pouvez le voir, l’implémentation de l’interface Function est désormais intégrée au niveau de la déclaration de la variable resultaLambda, plutôt que dans une classe séparée. C’est un peu plus court, et nous pouvons voir directement dans le code ci-dessus ce qu’elle fait.
Predicate
L’interface Java Predicate (java.util.function.Predicate) représente une fonction simple qui prend une seule valeur comme paramètre, et retourne un booléen. Voici à quoi ressemble la définition de l’interface fonctionnelle Predicate :
public interface Predicate {
boolean test(T t);
}
L’interface Predicate contient plus de méthodes que la méthode test(), mais le reste des méthodes sont des méthodes par défaut ou statiques que vous n’avez pas à implémenter.
Vous pouvez implémenter l’interface Predicate en utilisant une classe, comme ceci :
public class VerifierLeNull implements Predicate {
@Override
public boolean test(Object o) {
return o != null;
}
}
Vous pouvez également implémenter l’interface Java Predicate en utilisant une expression Lambda. Voici un exemple d’implémentation de l’interface Predicate à l’aide d’une expression lambda Java :
Predicate predicate = (value) -> value != null;
Cette implémentation lambda de l’interface Predicate fait effectivement la même chose que l’implémentation ci-dessus qui utilise une classe.
UnaryOperator
L’interface Java UnaryOperator est une interface fonctionnelle qui représente une opération qui prend un seul paramètre et renvoie un paramètre du même type. Voici un exemple d’implémentation de Java UnaryOperator :
UnaryOperator<Personne> unaryOperator =
(person) -> { personne.nom = "nouveau nom"; return personne; };
L’interface UnaryOperator peut être utilisée pour représenter une opération qui prend un objet spécifique comme paramètre, modifie cet objet et le renvoie à nouveau – éventuellement dans le cadre d’une chaîne de traitement de flux fonctionnel.
BinaryOperator
L’interface Java BinaryOperator est une interface fonctionnelle qui représente une opération qui prend deux paramètres et renvoie une seule valeur. Les deux paramètres et le type de retour doivent être identique.
L’interface Java BinaryOperator est utile pour mettre en œuvre des fonctions qui additionnent, soustraient, divisent, multiplient, etc. deux éléments du même type, et qui renvoient un troisième élément du même type.
Voici un exemple d’implémentation de l’interface BinaryOperator :
BinaryOperator<MaValeur> binaryOperator =
(valuer1, valuer2) -> { valuer1.add(valuer2); return valuer1; };
Supplier
L’interface Java Supplier est une interface fonctionnelle qui représente une fonction qui fournit une valeur X. L’interface Supplier peut également être considérée comme une interface Factory. Voici un exemple d’implémentation de l’interface Java Supplier :
Supplier<Integer> supplier = () -> new Integer((int) (Math.random() * 1000D));
Cette implémentation de Java Supplier renvoie une nouvelle instance d’Integer avec une valeur aléatoire entre 0 et 1000.
Consumer
L’interface Java Consumer est une interface fonctionnelle qui représente une fonction qui consomme une valeur sans en retouner une autre valeur. Cette interface peut être utiliser pour l’impression d’une valeur, ou l’écriture dans un fichier, ou sur le réseau, etc. Voici un exemple d’implémentation de l’interface Java Consumer :
Consumer<Integer> consumer = (value) -> System.out.println(value);
Cette implémentation Java Consumer imprime la valeur qui lui est passée en paramètre dans System.out.