123 votes

Les expressions lambda ont-elles une utilisation autre que la sauvegarde de lignes de code?

Les expressions lambda ont-elles une utilisation autre que la sauvegarde de lignes de code?

Les lambdas offrent-ils des fonctionnalités spéciales 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());
 

126voto

Holger Points 13789

Les expressions Lambda ne change pas l'ensemble des problèmes que vous pouvez résoudre avec Java en général, mais certainement faire de résoudre certains des problèmes de plus facile, il suffit pour la même raison, nous ne sommes pas de la programmation en langage d'assemblage plus. Enlever les tâches redondantes à partir du programmeur de travail rend la vie plus facile et vous permet de faire des choses que vous n'auriez même pas le toucher sinon, juste pour la quantité de code, vous avez à produire (manuellement).

Mais les expressions lambda sont pas seulement économiser de lignes de code. Les expressions Lambda vous permettent de définir des fonctions, quelque chose pour laquelle vous pouvez utiliser les classes internes anonymes comme une solution de contournement avant, c'est pourquoi vous pouvez remplacer les classes internes anonymes dans ces cas, mais pas en général.

Plus particulièrement, les expressions lambda sont définies indépendamment de l'interface fonctionnelle, ils seront convertis, donc il n'y a pas hérité membres pourraient accéder, de plus, ils ne peuvent pas accéder à l'instance du type de mise en œuvre de l'interface fonctionnelle. Au sein d'une expression lambda, this et super ont la même signification que dans le contexte environnant, voir aussi cette réponse. Aussi, vous ne pouvez pas créer de nouvelles variables locales à l'occultation des variables locales du contexte environnant. Pour la tâche de définir une fonction, cela enlève beaucoup de sources d'erreur, mais cela signifie aussi que pour les autres cas, il peut être anonyme intérieur des classes qui ne peuvent pas être convertis à une expression lambda, même si la mise en œuvre d'une interface fonctionnelle.

En outre, la construction new Type() { … } garanties pour produire une nouvelle distincte de l'instance (en tant que new le fait toujours). Anonyme intérieur des instances de classe toujours garder une référence à leur extérieur de l'instance si elle est créée dans un non-static contexte. En revanche, les expressions lambda capturer uniquement une référence à l' this lorsque cela est nécessaire, c'est à dire si elles accèdent this ou un non-static membre. Et ils produisent des instances d'une intentionnellement quelconque identité, qui permet la mise en œuvre de décider au moment de l'exécution que de réutiliser les instances existantes (voir aussi "Ne une expression lambda de créer un objet sur le tas à chaque fois qu'il est exécuté?").

Ces différences s'appliquent à votre exemple. Votre anonyme intérieure de construire sera toujours de produire une nouvelle instance, il peut également capturer une référence à l'extérieur de l'instance, alors que votre (Developer o1, Developer o2) -> o1.getName().compareTo(o2.getName()) est un non-capture d'une expression lambda, qui évaluent à un singleton dans typique des implémentations. De plus, il ne produit pas d' .class le fichier sur votre disque dur.

Étant donné les différences en ce qui concerne à la fois, de la sémantique et de la performance, des expressions lambda peut changer la façon dont les programmeurs vont résoudre certains problèmes dans l'avenir, bien sûr, également en raison de la nouvelle Api d'embrasser les idées de la programmation fonctionnelle en utilisant les nouvelles fonctionnalités du langage. Voir aussi Java 8 lambda expression et des valeurs de première classe.

56voto

Jared Smith Points 101

Les langages de programmation ne sont pas pour les machines à exécuter.

Ils sont pour les programmeurs de pense dans.

Les langues sont d'une conversation avec un compilateur pour transformer nos pensées en quelque chose qu'une machine peut exécuter. L'une des principales plaintes au sujet de Java à partir de gens qui viennent d'autres langues (ou congé d' elle pour d'autres langues) utilisé pour être que les forces d'un certain modèle mental sur le programmateur (c'est à dire tout est une classe).

Je ne vais pas peser sur que c'est bon ou mauvais, tout est compromis. Mais Java 8 lambdas permettre aux programmeurs de penser en termes de fonctions, qui est quelque chose que vous ne pouvaient pas le faire en Java.

C'est la même chose qu'une procédure programmeur apprendre à penser en termes de classes quand ils viennent à Java: vous les voyez passer progressivement de classes qui sont glorifiés et des structs ont 'helper' classes avec un tas de méthodes statiques et de passer à quelque chose qui ressemble plus à une utilisation rationnelle de conception OO (mea culpa).

Si vous venez de penser à eux comme un moyen plus court pour exprimer les classes internes anonymes, alors vous n'allez probablement pas à les trouver très impressionnant de la même manière que la procédure programmeur ci-dessus probablement ne pense pas que les classes ont été une grande amélioration.

40voto

Eran Points 35360

Enregistrement de lignes de code peut être considérée comme une nouvelle fonctionnalité, si elle vous permet de vous écrire une partie substantielle de la logique dans une plus courte et plus claire, ce qui prend moins de temps pour les autres à lire et à comprendre.

Sans expressions lambda (et/ou de la méthode références) Stream pipelines aurait été beaucoup moins lisible.

Pensons, par exemple, comment les suivantes Stream pipeline aurait ressemblé si vous avez remplacé chaque expression lambda avec une classe anonyme instance.

List<String> names =
    people.stream()
          .filter(p -> p.getAge() > 21)
          .map(p -> p.getName())
          .sorted((n1,n2) -> n1.compareToIgnoreCase(n2))
          .collect(Collectors.toList());

Il 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 sujettes à erreur. Il est également plus difficile à comprendre.

Et ce qui est relativement court pipeline.

Pour faire de ce lisible sans les expressions lambda et de la méthode références, vous auriez dû définir des variables qui détiennent les différentes interface fonctionnelle instances utilisé ici, qui se sont séparés de la logique de la canalisation, ce qui rend plus difficile à comprendre.

8voto

alseether Points 1617

Interne itération

Lors de l'itération Java Collections, la plupart des développeurs ont tendance à obtenir un élément, puis processus d' elle. C'est, prendre l'élément en question, puis de l'utiliser, ou réinsérer, etc. Avec pré-8 versions de Java, vous pouvez mettre en œuvre un intérieur classe et de 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);

Des comportements comme arguments

Imagine 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 Prédicat de l'interface que vous pouvez faire mieux comme ça:

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'appelant comme:

sumAll(numbers, n -> n % 2 == 0);

Source: DZone - Pourquoi Nous avons Besoin des Expressions Lambda en Java

4voto

holi-java Points 15887

Il existe de nombreux avantages de l'utilisation des expressions lambda au lieu de l'intérieur de la classe suivante comme ci-dessous:

  • Rendre le code plus léger et expressif, sans introduire plus de la syntaxe du langage de la sémantique. vous avez déjà donné un exemple dans votre question.

  • En utilisant des lambdas vous êtes heureux de la programmation fonctionnelle-style opérations sur les flux d'éléments, tels que réduire la carte des transformations sur les collections. voir java.util.fonction & java.util.flux de paquets de documentation.

  • Il n'y a pas de physique des classes de fichier généré pour les lambdas par le compilateur. Ainsi, il permet à vos applications livrées plus petits. Comment la Mémoire attribue à lambda?

  • Le compilateur d'optimiser lambda création, si l'lambda n'a pas accès aux variables de son champ d'application, ce qui signifie que le lambda instance de créer uniquement une fois par la JVM. pour plus de détails, vous pouvez voir @Holger la réponse à la question Est la méthode de référence la mise en cache une bonne idée en Java 8? .

  • Les Lambdas peuvent implémente multi marqueur interfaces en plus de l'interface fonctionnelle, mais les anonymes, les classes internes ne peuvent pas implémente plusieurs interfaces, par exemple:

    //                 v--- create the lambda locally.
    Consumer<Integer> action = (Consumer<Integer> & Serializable) it -> {/*TODO*/};
    

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X