1. Méthodes de fonction

Si une interface n'a qu'une seule méthode , une variable de ce type d'interface peut se voir attribuer une valeur donnée par une expression lambda (fonction lambda). Ces interfaces sont devenues connues sous le nom d' interfaces fonctionnelles (après que Java ait ajouté la prise en charge des fonctions lambda).

Par exemple, Java a l' Consumer<Type>interface, qui a la accept(Type obj)méthode. Pourquoi cette interface est-elle nécessaire ?

Dans Java 8, les collections ont une forEach()méthode qui vous permet d'effectuer une action pour chaque élément de la collection . Et ici, l' Consumer<T>interface fonctionnelle est utilisée pour transmettre l'action à la forEach()méthode.

Voici comment afficher tous les éléments d'une collection :

ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "Hello", "how's", "life?");

list.forEach( (s) -> System.out.println(s) );
Afficher tous les éléments d'une collection (à l'aide d'une expression lambda)

Le compilateur convertira le code ci-dessus en code ci-dessous :

ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "Hello", "how's", "life?");

list.forEach(new Consumer<String>()
{
   public void accept(String s)
   {
      System.out.println(s));
   }
});
Afficher tous les éléments d'une collection (à l'aide d'une classe anonyme)

La première version est nettement plus courte que la seconde. Et tandis que le code avec des expressions lambda est difficile à lire, le code avec des classes internes anonymes est parfois encore plus difficile à lire.



2. Référence de la méthode

Cependant, notre code d'expression lambda peut être écrit encore plus court.

Tout d'abord, vous pouvez omettre les parenthèses autour du sparamètre :

list.forEach( (s) -> System.out.println(s) );
Avant
list.forEach( s -> System.out.println(s) );
Après

Cela ne peut être fait que s'il y a un paramètre . S'il y a plusieurs paramètres, vous devez utiliser des parenthèses .

Et deuxièmement, vous pouvez l'écrire comme ceci :

list.forEach( System.out::println );
Notation la plus compacte

C'est exactement la même notation. Notez qu'il n'y a pas de parenthèses après le println.

Ici, nous avons le même code — un appel de méthode :

object::method
x -> object.method(x)

Pensez-y : nous voulions effectuer une action pour chaque élément de la listcollection. Si l'action est un appel de fonction unique (tel que println()), il est logique de simplement passer la fonction à la méthode en tant que paramètre.

Mais comment expliquer au compilateur qu'on veut passer la méthode plutôt que de l'appeler ? Pour ce faire, au lieu de l'opérateur point, nous utilisons deux deux-points avant le nom de la méthode. Un seul deux-points est déjà utilisé pour indiquer l'opérateur ternaire.

C'est la notation la plus simple et la plus compacte.



3. Constructeur

Les références de méthode avec des doubles-points sont très pratiques lorsque nous travaillons avec des flux d'E/S. Vous verrez cela un peu plus tard.

En attendant, parlons de 3 façons populaires de passer une référence de méthode :

Référence à une méthode d'un objet

Pour passer une référence à une méthode d'un objet, vous devez écrire quelque chose comme . Ce code est équivalent à .object::method
x -> object.method(x)

Les variables spéciales thiset superpeuvent être utilisées comme objet.

Référence à une méthode d'une classe

Pour passer une référence à une méthode statique, vous devez écrire quelque chose comme . Ce code est converti en code commeclass::methodx -> class.method(x);

Référence à un constructeur

Un constructeur se comporte de la même manière qu'une méthode de classe statique, vous pouvez donc également passer une référence à un constructeur. Voici à quoi ça ressemble : .class::new

Par exemple, vous pouvez contourner l'effacement de type pour les collections et transmettre à la toArray()méthode une référence à un constructeur qui créera le tableau souhaité :toArray(int[]::new);