Les expressions lambda ont-elles une utilisation autre que la sauvegarde de lignes de code?
Y a-t-il des fonctionnalités spéciales fournies par lambdas qui résolvent des problèmes difficiles à résoudre? L'utilisation typique que j'ai vue est qu'au lieu d'écrire ceci:
Comparator<Developer> byName = new Comparator<Developer>() {
@Override
public int compare(Developer o1, Developer o2) {
return o1.getName().compareTo(o2.getName());
}
};
Nous pouvons utiliser une expression lambda pour raccourcir le code:
Comparator<Developer> byName =
(Developer o1, Developer o2) -> o1.getName().compareTo(o2.getName());
Les expressions lambda ne changent pas l’ensemble des problèmes que vous pouvez résoudre avec Java en général, mais facilitent définitivement la résolution de certains problèmes, juste pour la même raison, nous ne programmons plus en langage Assembly. Supprimer les tâches redondantes du travail du programmeur facilite la vie et permet de faire des choses que vous ne toucheriez même pas autrement, juste pour la quantité de code que vous auriez à produire (manuellement).
Mais les expressions lambda ne sont pas simplement des lignes de code. Les expressions lambda vous permettent de définir des fonctions , pour lesquelles vous pouvez utiliser des classes internes anonymes comme solution de contournement, c'est pourquoi vous pouvez remplacer des classes internes anonymes dans ces classes. cas, mais pas en général.
Plus particulièrement, les expressions lambda sont définies indépendamment de l'interface fonctionnelle vers laquelle elles seront converties. Par conséquent, aucun membre hérité auquel elles pourraient accéder n'a accès. De plus, elles ne peuvent pas accéder à l'instance du type implémentant l'interface fonctionnelle. Dans une expression lambda, this
et super
ont la même signification que dans le contexte, voir aussi cette réponse . En outre, vous ne pouvez pas créer de nouvelles variables locales en masquant les variables locales du contexte environnant. Pour la tâche envisagée de définition d'une fonction, cela supprime de nombreuses sources d'erreur, mais cela implique également que, dans d'autres cas d'utilisation, il peut exister des classes internes anonymes qui ne peuvent pas être converties en une expression lambda, même si une interface fonctionnelle est implémentée.
De plus, la construction new Type() { … }
garantit de produire une nouvelle instance distincte (comme le fait toujours new
.). Les instances de classe interne anonymes conservent toujours une référence à leur instance externe si elles sont créées dans un contexte non -static
. En revanche, les expressions lambda capturent uniquement une référence à this
lorsque cela est nécessaire, c’est-à-dire s’ils accèdent à this
ou à un membre non -static
. Et ils produisent des occurrences d'une identité intentionnellement non spécifiée, ce qui permet à l'implémentation de décider à l'exécution si les occurrences existantes doivent être réutilisées (voir aussi “ ne expression lambda crée-t-elle un objet sur le tas chaque fois qu'elle est exécutée? ” ).
Ces différences s'appliquent à votre exemple. Votre construction de classe interne anonyme produira toujours une nouvelle instance. Elle peut également capturer une référence à l'instance externe, tandis que votre (Developer o1, Developer o2) -> o1.getName().compareTo(o2.getName())
est une expression lambda sans capture qui sera évaluée à un singleton dans les implémentations typiques. De plus, il ne produit pas de fichier .class
sur votre disque dur.
Étant donné les différences entre la sémantique et la performance, les expressions lambda peuvent changer la façon dont les programmeurs vont résoudre certains problèmes à l'avenir, bien sûr, également en raison des nouvelles API englobant des idées de programmation fonctionnelle utilisant les nouvelles fonctionnalités du langage. Voir aussi expression lambda Java 8 et valeurs de première classe .
Les langages de programmation ne sont pas destinés aux machines à exécuter.
Ce sont les programmeurs qui doivent penser dans.
Les langues sont une conversation avec un compilateur pour transformer nos pensées en quelque chose qu'une machine peut exécuter. Une des principales plaintes à propos de Java de la part de personnes qui lui parlent d'autres langues (ou permission il pour autres langues) était autrefois ce que - force un certain modèle mental sur le programmeur (ie tout est une classe).
Je ne vais pas me demander si c'est bon ou mauvais: tout est un compromis. Mais Java 8 lambdas permettent aux programmeurs de penser en termes de fonctions, ce que vous ne pouviez pas faire auparavant en Java.
C’est la même chose qu’un programmeur procédural apprenant à penser en termes de classes quand ils arrivent en Java: vous les voyez progressivement quitter des classes qui sont des structures glorifiées et qui ont des classes "auxiliaires" avec beaucoup de statique méthodes et passez à quelque chose qui ressemble plus à un design rationnel OO (mea culpa).
Si vous les considérez comme un moyen plus rapide d’exprimer des classes internes anonymes, vous ne les trouverez probablement pas très impressionnants de la même manière que le programmeur procédural ci-dessus ne pensait probablement pas que les classes constituaient une grande amélioration.
L'enregistrement de lignes de code peut être considéré comme une nouvelle fonctionnalité si cela vous permet d'écrire une partie substantielle de la logique de manière plus courte et plus claire, ce qui prend moins de temps à lire et à comprendre pour les autres.
Sans les expressions lambda (et/ou les références de méthodes), Stream
les pipelines auraient été beaucoup moins lisibles.
Imaginez, par exemple, à quoi ressemblerait le pipeline Stream
suivant si vous aviez remplacé chaque expression lambda par une instance de classe anonyme.
List<String> names =
people.stream()
.filter(p -> p.getAge() > 21)
.map(p -> p.getName())
.sorted((n1,n2) -> n1.compareToIgnoreCase(n2))
.collect(Collectors.toList());
Ce serait:
List<String> names =
people.stream()
.filter(new Predicate<Person>() {
@Override
public boolean test(Person p) {
return p.getAge() > 21;
}
})
.map(new Function<Person,String>() {
@Override
public String apply(Person p) {
return p.getName();
}
})
.sorted(new Comparator<String>() {
@Override
public int compare(String n1, String n2) {
return n1.compareToIgnoreCase(n2);
}
})
.collect(Collectors.toList());
C'est beaucoup plus difficile à écrire que la version avec les expressions lambda, et c'est beaucoup plus sujet aux erreurs. C'est aussi plus difficile à comprendre.
Et ceci est un pipeline relativement court.
Pour que cela soit lisible sans expressions lambda ni références de méthodes, il aurait fallu définir des variables contenant les différentes instances d'interface fonctionnelle utilisées ici, ce qui aurait divisé la logique du pipeline en la rendant plus difficile à comprendre.
Lors de l'itération de Java Collections, la plupart des développeurs ont tendance à obtenir un élément, puis à traiter ça. Enlevez cet élément, puis utilisez-le, ou réinsérez-le, etc. Avec les versions antérieures à la version 8 de Java, vous pouvez implémenter une classe interne et faire quelque chose comme:
numbers.forEach(new Consumer<Integer>() {
public void accept(Integer value) {
System.out.println(value);
}
});
Maintenant, avec Java 8, vous pouvez faire mieux et moins verbeux avec:
numbers.forEach((Integer value) -> System.out.println(value));
ou mieux
numbers.forEach(System.out::println);
Devinez le cas suivant:
public int sumAllEven(List<Integer> numbers) {
int total = 0;
for (int number : numbers) {
if (number % 2 == 0) {
total += number;
}
}
return total;
}
Avec Java 8 interface de prédicat , vous pouvez faire mieux comme ceci:
public int sumAll(List<Integer> numbers, Predicate<Integer> p) {
int total = 0;
for (int number : numbers) {
if (p.test(number)) {
total += number;
}
}
return total;
}
L'appeler comme:
sumAll(numbers, n -> n % 2 == 0);
Source: DZone - Pourquoi nous avons besoin des expressions lambda en Java
Il y a de nombreux avantages à utiliser lambdas au lieu de la classe intérieure, comme ci-dessous:
Rendez le code plus compact et plus expressif sans introduire davantage de sémantique de syntaxe de langage. vous avez déjà donné un exemple dans votre question.
En utilisant lambdas, vous êtes prêt à programmer avec des opérations de style fonctionnel sur des flux d'éléments, telles que des transformations de réduction de carte sur des collections. voir Java.util.function & Java.util.stream documentation sur les packages.
Il n'y a pas de fichier de classes physiques généré pour lambdas par le compilateur. Ainsi, vos applications livrées sont réduites. Comment la mémoire attribue-t-elle à lambda?
Le compilateur optimisera la création lambda si le lambda n’accède pas aux variables hors de son champ, ce qui signifie que l’instance lambda ne crée qu’une fois par la machine virtuelle Java. pour plus de détails, vous pouvez voir la réponse de @ Holger à la question La référence de méthode met-elle en cache une bonne idée dans Java 8? .
Lambdas peut implémenter des interfaces multi-marqueurs en plus de l'interface fonctionnelle, mais les classes internes anonymes ne peuvent pas implémenter plus d'interfaces, par exemple:
// v--- create the lambda locally.
Consumer<Integer> action = (Consumer<Integer> & Serializable) it -> {/*TODO*/};
Une chose que je ne vois pas encore mentionnée est qu'un lambda vous permet de définir une fonctionnalité là où il est utilisé.
Donc, si vous avez une fonction de sélection simple, vous n'avez pas besoin de la placer dans un endroit séparé avec un tas de passe-partout, vous écrivez simplement un lambda qui est concis et pertinent localement.
Pour répondre à votre question, le fait est que lambdas ne pas vous permet de faire tout ce que vous ne pouviez pas faire avant Java-8, mais vous permet plutôt d'en écrire plus concis code. L'avantage de ceci est que votre code sera plus clair et plus flexible.
Oui de nombreux avantages sont là.
Les Lambda ne sont que du sucre syntaxique pour les classes anonymes.
Avant lambdas, les classes anonymes peuvent être utilisées pour obtenir la même chose. Chaque expression lambda peut être convertie en une classe anonyme.
Si vous utilisez IntelliJ IDEA, la conversion peut être effectuée à votre place: