- Apprenez à connaître l'interface fonctionnelle
Commencez par introduire l'interface fonctionnelle. Une interface fonctionnelle est une interface qui n'a qu'une seule méthode abstraite (il peut y avoir plusieurs méthodes non abstraites). Les interfaces fonctionnelles courantes incluent Runnable, Comparable, etc.
@FunctionalInterface
public interface Runnable {
void run();
}
public interface Comparable<T> {
int compareTo(T var1);
}
Après jdk1.8, des interfaces fonctionnelles communes (Function, Consumer, Predicate, Supplier, BiFunction, IntBinaryOperator) et leurs interfaces fonctionnelles dérivées sous le package java.util.function ont été ajoutées.
public interface Consumer<T> {
void accept(T var1);
}
public interface Function<T, R> {
R apply(T var1);
}
public interface Predicate<T> {
boolean test(T var1);
}
public interface Supplier<T> {
T get();
}
public interface BiFunction<T, U, R> {
R apply(T var1, U var2);
}
public interface IntBinaryOperator {
int applyAsInt(int var1, int var2);
}
Les interfaces fonctionnelles ci-dessus utilisent toutes des génériques, la différence réside dans la signature de la méthode. Par conséquent, cet article utilise uniquement les deux premières interfaces fonctionnelles comme exemples. Les autres principes sont similaires. Tout d'abord, prenez Function comme exemple:
public class FunctionClient {
String clientPrint() {
return "this is functionClient test";
}
public static void main(String[] args) {
FunctionInterface functionInterface = new FunctionInterface();
//第一种写法
System.out.println(functionInterface.functionTest(FunctionClient::clientPrint));
//第二种写法
System.out.println((String) functionInterface.functionTest(functionClient -> functionClient.clientPrint()));
}
}
class FunctionInterface {
FunctionClient functionClient = new FunctionClient();
<R> R functionTest(Function<FunctionClient, R> function) {
return function.apply(functionClient);
}
}
L'interface fonctionnelle utilisée ici est Function, qui correspond à la méthode apply. Le FunctionTest de la classe FunctionalInterface est utilisé pour simuler l'interface que les développeurs de SDK donnent aux développeurs de logiciels. Le paramètre d'entrée de cette interface est Function et le paramètre de sortie est R :
<R> R functionTest(Function<FunctionClient, R> function) {
return function.apply(functionClient);
}
Lorsque l'objet function appelle la méthode apply, il exécute en fait le code derrière la flèche, qui est functionClient.clientPrint (). Le paramètre d'entrée de functionTest est l'expression "functionClient -> functionClient.clientPrint ())". Le type exécuté sur le côté droit de la flèche est le type de valeur de retour de l'interface fonctionnelle. Puisque functionClient.clientPrint () renvoie String, donc voici R fait également spécifiquement référence au type String.
En plus de l'objet FunctionClient spécifié ci-dessus, est-il possible d'utiliser des types ordinaires au lieu de la classe FunctionClient sur le côté gauche de la flèche?
public class FunctionClient {
String clientPrint() {
return "this is functionClient test";
}
public static void main(String[] args) {
FunctionInterface functionInterface = new FunctionInterface();
Function<String, String> function = "This is left param." -> new FunctionClient().clientPrint();//报错行
System.out.println(functionInterface.functionTest(function));
}
}
class FunctionInterface {
<R> R functionTest(Function<String, R> function) {
return function.apply("This is FunctionalInterface");
}
}
Le code ci-dessus échouera à se compiler car Function ne peut pas accepter le type String et d'autres constantes. Étant donné que le côté gauche de la flèche représente le paramètre d'entrée d'une méthode, le paramètre d'entrée ne peut être qu'une variable pour la méthode, donc une erreur sera signalée si elle est directement définie comme constante.
Alors, comment représenter une scène sans paramètres? Définissez simplement une variable non déclarée sur le côté gauche de la flèche:
Function<String, String> function = s -> new FunctionClient().clientPrint();
Pour l'affectation d'une interface fonctionnelle, le côté gauche de la flèche n'a besoin que de passer le nombre correspondant de noms de variables non affectés. S'il n'y a pas de paramètre d'entrée, utilisez simplement ().
S'il n'y a qu'une valeur de retour et aucun paramètre, vous pouvez utiliser l'interface Fournisseur:
public class FunctionClient {
String clientPrint() {
return "this is functionClient test";
}
public static void main(String[] args) {
FunctionInterface functionInterface = new FunctionInterface();
Supplier<String> function = () -> new FunctionClient().clientPrint();
System.out.println(functionInterface.functionTest(function));
}
}
class FunctionInterface {
<R> R functionTest(Supplier<R> function) {
return function.get();
}
}
- Quels sont les avantages de la programmation fonctionnelle?
Simplifiez le code, par exemple:
Runnable runnable1 = new Runnable() {
@Override
public void run() {
System.out.println("Runnable");
}
};
//第一次简化,函数式编程(语句lambda形式)
Runnable runnable2 = () -> {
System.out.println("Runnable");
};
//第二次简化,函数式编程(表达式lambda形式)
Runnable runnable3 = () -> System.out.println("Runnable");
又比如:
Consumer<Integer> consumer = i -> System.out.println(i);
List<Integer> arrayList = new ArrayList<>(){
{
add(1);
add(2);
add(4);
}
};
arrayList.forEach(consumer);
- La relation entre lambda, la programmation fonctionnelle et les rappels
1. Lambda est une implémentation d'une interface fonctionnelle, qui peut être comprise comme du sucre syntaxique.
2. Le rappel n'a pas de limite sur le nombre de méthodes dans l'interface, mais les interfaces fonctionnelles ne peuvent spécifier qu'une seule méthode abstraite.
3. Les définitions de méthode de programmation fonctionnelle et de rappel sont toutes deux sur l'appelant, et les entrées des deux sont des interfaces, mais l'une d'elles est une interface normale et l'autre est une interface fonctionnelle.
4. La programmation fonctionnelle et les méthodes de rappel sont toutes deux exécutées sur l'appelé. La première exécute la seule méthode correspondante, telle que apply, tandis que la seconde peut correspondre à plusieurs méthodes, il suffit d'exécuter la méthode correspondante.
5. Les deux peuvent utiliser lambda pour définir l'implémentation de la méthode sur l'appelant.
6. La mise en œuvre de la programmation fonctionnelle est un rappel spécial.