Nouvelle grammaire introduite dans Java 8. Simplifiez la description du traitement en omettant le contenu décrit à l'aide de la classe locale et de la classe anonyme. Comme exemples typiques, les méthodes de Collections.sort et Stream API ont bénéficié.
Les expressions Lambda utilisent un mécanisme appelé classes locales et classes anonymes.
Une classe locale est un mécanisme qui peut être utilisé en déclarant une classe lors du traitement d'une méthode.
public static void main(String[] args) {
class Local {
public void sayHello() {
System.out.println("Hello!");
}
}
Local local = new Local();
local.sayHello(); // Hello!
}
Vous pouvez également définir une classe locale qui implémente l'interface.
public static void main(String[] args) {
class Local implements Runnable {
@Override
public void run() {
System.out.println("Hello Lambda!");
}
}
Runnable runner = new Local();
runner.run(); // Hello Lambda!
}
Ensuite, jetons un œil à la classe anonyme.
La classe anonyme est un mécanisme qui omet la déclaration de la classe locale qui implémente l'interface. Voici un exemple de classe anonyme qui implémente l'interface Runnable.
public static void main(String[] args) {
Runnable runner = new Runnable() {
@Override
public void run() {
System.out.println("Hello Lambda!");
}
};
runner.run(); //Hello Lambda!
}
Il semble que vous créez une instance de l'interface Rannable, mais que vous créez en fait une instance d'une classe anonyme qui implémente l'interface Rannable. Enfin, regardons l'expression lambda.
Dans la classe anonyme, "new Runnable () {}" et "public void run" sont omis pour créer une expression lambda.
public static void main(String[] args) {
Runnable runner = () -> { System.out.println("Hello Lambda!"); };
runner.run(); //Hello Lambda!
}
Le premier () représente l'argument de la méthode run, et le contenu de-> {} est le contenu de l'implémentation de la méthode run. La variable runner se voit attribuer une instance d'une classe anonyme qui implémente Runnable. En d'autres termes, une expression lambda est une expression qui crée une instance qui implémente l'interface.
Au fait, si j'omets "new Runnable () {}", je ne sais pas quel type d'instance créer. Java dispose d'un mécanisme pour déduire automatiquement en fonction du type de variable à affecter. Ce mécanisme est appelé inférence de type.
De plus, si vous omettez "public void run", vous ne saurez pas quelle méthode remplacer pour une interface qui a plusieurs méthodes définies. Par conséquent, les expressions lambda ne peuvent utiliser des interfaces qu'avec une seule méthode abstraite.
L'interface Rannable seule ne peut créer que des expressions lambda sans arguments et sans valeur de retour. Si vous souhaitez le créer sous une autre forme, une interface fonctionnelle a été ajoutée, alors utilisez-la.
Une interface fonctionnelle est une interface qui peut être affectée à des expressions lambda et des références de méthode.
La condition d'une interface fonctionnelle est, grosso modo, une interface qui n'a qu'une seule méthode abstraite définie. Les méthodes statiques et les méthodes par défaut peuvent être incluses (ignorées comme condition de l'interface fonctionnelle). De plus, si une méthode publique de la classe Object est définie comme méthode abstraite dans l'interface, cette méthode est également ignorée. (Une interface qui satisfait à cette condition est désormais appelée "interface fonctionnelle" dans JDK1.8)
De nombreuses nouvelles interfaces ont été ajoutées sous le package java.util.function de SE8. https://docs.oracle.com/javase/jp/8/docs/api/java/util/function/package-summary.html
Cette fois, je présenterai les interfaces les plus fréquemment utilisées.
2-1. Function<T, R> Function est une interface fonctionnelle pour la conversion des valeurs. Dans Function \ <T, R>, T spécifie le type de l'argument de la méthode et R spécifie le type de la valeur de retour. Prend un argument, le convertit (le calcule) et renvoie une autre valeur. La méthode est R appliquer (T).
Function<Integer, String> asterisker = (i) -> { return "*"+ i; };
String result = asterisker.apply(10);
System.out.println(result); // *10
2-2. Consumer<T>
Consumer est une interface fonctionnelle pour recevoir des arguments et les utiliser pour le traitement.
T dans Consumer \
Consumer<String> buyer = (goods) -> { System.out.println(goods + "j'ai acheté"); };
buyer.accept("balle de riz"); // balle de rizを購入しました。
2-3. Predicate<T>
Predicate est une interface fonctionnelle pour faire des jugements.
T dans Predicate \
Predicate<String> checker = (s)-> { return s.equals("Java"); };
boolean result = checker.test("Java");
System.out.println(result); //true
Le code suivant décrit l'API Stream à l'aide d'expressions lambda.
List<Integer> numbers = List.of(3, 1, -4, 1, -5, 9, -2, 6, 5, 3, 5);
numbers.stream()
.filter(number -> Math.abs(number) >= 5)
.forEach(System.out::println);
Le résultat de sortie est le suivant.
-5
9
6
5
5
Ensuite, regardons le code écrit sans utiliser l'expression lambda.
List<Integer> numbers = List.of(3, 1, -4, 1, -5, 9, -2, 6, 5, 3, 5);
numbers.stream()
.filter(new Predicate<Integer>() {
@Override
public boolean test(Integer number) {
return Math.abs(number) >= 5;
}
})
.forEach(new Consumer<Integer>() {
@Override
public void accept(Integer number) {
System.out.println(number);
}
});
Comme introduit dans la décomposition de l'expression lambda, le nombre de descriptions de méthodes pour créer et exécuter des interfaces augmente. La quantité de code écrit a augmenté et les perspectives de traitement sont devenues très mauvaises. De cette manière, il est utilisé pour rendre la description de traitement concise et facile à comprendre.
Explique la grammaire des expressions lambda. Voici la grammaire de base de l'expression lambda.
(argument) -> {En traitement; }
Ce qui suit est écrit selon cette grammaire.
// (1)S'il y a des arguments et des valeurs de retour
(Integer number) -> {
return Math.abs(number) >= 5;
}
// (2)S'il n'y a pas de valeur de retour
(Integer number) -> {
System.out.println(number);
}
// (3)S'il n'y a pas d'arguments ou de valeurs de retour
() -> {
System.out.println("Hello!");
}
(1) est un exemple d'avoir un argument et une valeur de retour comme Predicate. Le traitement est effectué à l'aide du nombre spécifié par l'argument et la valeur de retour est renvoyée. (2) est un exemple sans valeur de retour comme Consumer. Dans ce cas, il n'est pas nécessaire d'écrire une instruction return. Pour le traitement sans arguments, comme dans (3), décrivez la partie argument dans (). Par exemple, java.lang.Runnable.
De plus, dans les expressions lambda, le type d'argument peut être omis. Et vous ne pouvez omettre les parenthèses () autour de l'argument que si vous n'avez qu'un seul argument. Il ne peut pas être omis s'il n'y a pas d'argument ou s'il y en a deux ou plus. L'application de cette règle à (1) et (3) donne:
// (1)Parce qu'il n'y a qu'un seul argument( )Peut être omis
number -> {
return Math.abs(number) >= 5;
}
// (3)Parce qu'il n'y a pas d'argument( )Ne peut pas être omis
() -> {
System.out.println("Hello!");
}
De plus, s'il n'y a qu'une seule ligne à traiter, vous pouvez omettre le crochet du milieu {}, le retour et le point-virgule à la fin de la phrase. Les abréviations de (1) à (3) sont les suivantes.
// (1)S'il y a des arguments et des valeurs de retour
number -> Math.abs(number) >= 5
// (2)S'il n'y a pas de valeur de retour
number -> System.out.println(number)
// (3)S'il n'y a pas d'arguments ou de valeurs de retour
() -> System.out.println("Hello!")
Enfin, l'argument lui-même peut être omis en utilisant la référence de méthode uniquement lorsque le contenu du traitement est un appel de méthode et que l'argument est déterminé de manière unique. La référence de méthode a la syntaxe suivante.
nom de la classe::Nom de la méthode
Cette référence de méthode ne peut être appliquée que dans (2). Si (2) est décrit à l'aide de la référence de méthode, ce sera comme suit.
System.out::println
La méthode System.out.println est une méthode qui ne prend qu'un seul argument, et il est clair que la valeur de l'argument Integer est passée, donc une référence de méthode est disponible. D'un autre côté, dans (1), la référence de méthode ne peut pas être utilisée car il y a un jugement de grandeur> = 5 après l'appel de méthode. De plus, dans (3), puisque la valeur "Hello!" Est spécifiée pour l'argument, on ne peut pas dire que l'argument est déterminé de manière unique, et la référence de méthode ne peut pas non plus être utilisée pour cela.
Dans cet esprit, jetons un coup d'œil au traitement du flux plus tôt.
List<Integer> numbers = List.of(3, 1, -4, 1, -5, 9, -2, 6, 5, 3, 5);
numbers.stream()
.filter(number -> Math.abs(number) >= 5)
.forEach(System.out::println);
Vous pouvez voir que les expressions lambda apprises en (1) et (2) sont incorporées dans streamAPI.
Comprendre les expressions Lambda Java 8 Java moderne appris avec les expressions Lambda et les API de flux - Le présent du langage Java qui change en incorporant des types de fonctions