Envoyé par
la.lune
Aussi, je suis je suis un peu perdu quand tu parles de correspondance entre signature de méthode et signature d'interface, ou bien tu veux parler de correspondance entre signature de méthode et signature de la méthode de l'interface fonctionnelle.
Oui je voulais bien parler de la signature de la méthode de l'interface fonctionnelle.
Une lambda (ou une référence de méthode) ne peut être associé qu'à une interface fonctionnelle, et il faut que la signature correspondent à celle de la méthode :
Par exemple :
ActionListener listener = (ActionEvent e) -> System.out.println("ActionEvent = " + e);
L'expression lambda a la signature suivante : "
void(ActionEvent)", ce qui correspond à la signature de la méthode de l'interface fonctionnelle
ActionListener qui défini la méthode suivante :
public void actionPerformed(ActionEvent e);
Au passage le "Type Inférence" rentre en jeu, ce qui nous permet de ne pas spécifier le type du paramètre dans la lambda :
ActionListener listener = (e) -> System.out.println("ActionEvent = " + e);
Et c'est la même chose pour les références de méthode. Si on a une méthode définie comme ceci :
1 2 3
| public static void onEvent(ActionEvent e) {
...
} |
On peut l'associer à un
ActionListener puisque la signature de la méthode concorde bien à la signature
void(ActionEvent) :
ActionListener listener = MaClasse::onEvent;
S'il s'agit d'une méthode d'instance :
1 2 3 4 5 6
| public class MaClasse {
public void doSomething(ActionEvent e) {
...
}
} |
On ne peut pas l'associer comme cela puisque l'instance se rajoute à la signature, qui devient
void(MaClasse,ActionEvent).
ActionListener listener = MaClasse::doSomething; // ERREUR
Pour que la signature corresponde, il faut lui associer une instance, par exemple :
1 2
| MaClasse cls = new MaClasse();
ActionListener listener = cls::doSomething; // OK |
Dans ce cas là l'instance ("cls" dans l'exemple) sera utilisée pour l'appel de la méthode, qui conserve du coup sa signature originale...
Et pour les méthodes c'est la même chose. Les méthodes qui utiliseront les lambdas n'ont rien de spécial, si ce n'est qu'elles utilisent une interface fonctionnelle. Mais même si cette notion est "nouvelle", cela ne l'est pas vraiment car l'API standard est déjà pleine d'interface fonctionnelle...
Du coup on pourra utiliser les lambdas/références de méthode avec des APIs existante, du moment qu'elles utilisent une interface fonctionnelle :
1 2 3
| MaClasse cls = new MaClasse();
JButton b = new JButton();
b.addActionListener(cls::doSomething); |
C'est le "Type inference" qui se chargera de vérifier le tout, c'est à dire :
- Récupérer le type du paramètre de addActionListener() (qui est justement un ActionListener).
- Vérifier qu'il s'agit bien d'une interface fonctionnelle (ce qui est le cas puisqu'elle ne comporte qu'une seule méthode)
- Vérifier que la signature de la lambda/référence de méthode concorde bien avec la signature de la méthode de l'interface fonctionnelle.
Et en cas de surcharge, le compilo ira chercher la version de la méthode qui correspond.
A noter également l'existence de référence de constructeur (en utilisant ::new), par exemple :
1 2 3 4 5 6 7 8 9 10 11 12 13
|
// Création d'un ThreadFactory via une classe anonyme :
Executors.newCachedThreadPool(new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
return new Thread(r);
}
});
// Création d'un ThreadFactory via une référence de constructeur :
Executors.newCachedThreadPool(Thread::new);
// Ceci utilisera le constructeur Thread(Runnable),
// qui correspond à la signature de la méthode de ThreadFactory |
a++
1 |
0 |