Pourquoi utiliser les expressions lambda
Jetons un coup d'œil à quelques exemples:
Le premier exemple consiste à exécuter une tâche dans un thread séparé, que nous implémentons habituellement comme suit:
Class Worker implémente Runnable {public void run () {for (int i = 0; i <100; i ++) dowork (); } ...} Worker W = new Worker (); nouveau thread (w) .start ();Le deuxième exemple est une méthode de comparaison de chaîne personnalisée (par longueur de chaîne), qui est généralement effectuée:
Class LongtComparator implémente le comparateur <string> {public int compare (String First, String Second) {return Integer.Compare (first.length (), second.length ()); }} Arrays.sort (Strings, new Longueurcomparator ());Dans le troisième exemple, dans Javafx, ajoutez un rappel à un bouton:
Button.SetonAction (New EventHandler <CactionEvent> () {Public Void Handle (ActionEvent Event) {System.out.println ("Merci pour CLICK!");}});Ces exemples ont une chose en commun, à savoir qu'ils définissent d'abord un bloc de code, le transmettent à un objet ou une méthode, puis l'exécutent. Avant les expressions Lambda, Java n'autorise pas le passage direct des blocs de code, car Java est orienté objet, donc un objet doit être transmis pour encapsuler le bloc de code à exécuter dans l'objet.
Syntaxe d'expression de lambda
La longueur de la longueur dans le deuxième exemple ci-dessus est exprimée en expression de lambda:
(String First, String Second) -> Integer.compare (first.length (), second.length ());
-> avant est la liste des paramètres, suivie du corps de l'instruction d'expression;
Si le corps d'une instruction d'expression est plus d'une ligne, le corps de l'instruction est écrit en {}, tout comme une fonction ordinaire:
(String First, String second) -> {if (first.length ()> seconde.length ()) {return 1; } else if (first.length () == second.length ()) {return 0; } else {return -1; }};S'il n'y a pas de paramètres, () doit encore être apporté avec vous. Par exemple, le premier exemple ci-dessus peut être exprimé comme:
() -> {for (int i = 0; i <1000; i ++) {dowork (); }}Si le type du paramètre peut être automatiquement déduit du contexte, vous pouvez omettre:
Comparator <string> comp = (premier, second) // identique à (String First, String second) -> Integer.Compare (first.length (), second.length ());
S'il n'y a qu'un seul paramètre et que le type peut être automatiquement déduit, les supports () peuvent également être omis:
// au lieu de (événement) -> ou (événement ActionEvent) -> EventHandler <CactionEvent> écouteur = événement -> System.out.println ("Merci pour cliquer!");Le type de valeur de retour de l'expression de lambda est automatiquement déduit, il n'a donc pas besoin d'être spécifié; Dans l'expression de Lambda, certaines branches conditionnelles ont des valeurs de retour, mais d'autres branches n'ont pas de valeurs de retour, ce qui n'est pas autorisé, tel que:
(x) -> {if (x> = 0) {return 1; }}De plus, la différence entre l'expression lambda et la déclaration lambda est que l'expression lambda n'a pas besoin d'écrire le mot-clé de retour. Java Runtime renvoie le résultat de l'expression comme valeur de retour, tandis que l'instruction Lambda est une expression écrite en {}, et le mot-clé de retour doit être utilisé, par exemple:
// Expression lambdacomparator <string> comp1 = (premier, second) -> Integer.Compare (first.length (), second.length ()); // instruction lambdacomparator <string> comp2 = (premier, second) -> {return Integer.comPare (first.length (), second.Length ());}; Interface fonctionnelle
Si une interface n'a qu'une seule méthode abstraite, elle est appelée
Interface fonctionnelle, comme Runnable, Comparator, etc.
Dans n'importe quel endroit où l'objet d'interface fonctionnelle est nécessaire, vous pouvez utiliser des expressions Lambda:
Arrays.sort (Words, (First, Second) -> Integer.Compare (First.Length (), Second.Length ()));
Ici, le deuxième paramètre de type () nécessite un objet de comparateur, et le comparateur est
Interface fonctionnelle, vous pouvez donc passer directement dans l'expression de lambda. Lorsque vous appelez la méthode compare () de l'objet, il s'agit d'exécuter le corps de l'instruction dans l'expression de Lambda;
Si l'instruction de l'expression de lambda lance une exception, la méthode abstraite correspondante dans l'interface fonctionnelle doit lancer l'exception, sinon il est nécessaire de prendre explicitement l'exception dans l'expression de lambda:
Runnable r = () -> {System.out.println ("-------"); essayez {thread.sleep (10); } catch (InterruptedException e) {// catch exception}}; callable <string> c = () -> {System.out.println ("----------"); Thread.Sleep (10); retour "";}; Référence de la méthode
Si les paramètres de l'expression de lambda sont passés sous forme de paramètres à une méthode et que leur effet d'exécution est le même, l'expression de lambda peut être exprimée en utilisant la référence de la méthode, et les deux méthodes suivantes sont équivalentes:
(x) -> System.out.println (x) System.out :: println
Parmi eux, System.out :: println est appelé référence de méthode.
La référence de la méthode se présente principalement sous trois formes:
Pour les deux premières méthodes, les paramètres d'expression LAMBDA correspondants et les paramètres de méthode sont les mêmes, comme:
System.out :: println (x) -> System.out.println (x) math :: Pow (x, y) -> math.pow (x, y)
Pour la troisième méthode, dans le corps de déclaration d'expression LambdA correspondant, le premier paramètre est utilisé comme objet, la méthode est appelée et d'autres paramètres sont utilisés comme paramètres de méthode, tels que:
String :: CompareToignoreCase (S1, S2) -> S1.comparetoignorecase (S2) 1.5 Référence du constructeur
La référence du constructeur est similaire à la référence de la méthode, mais c'est une méthode spéciale: nouveau. Le constructeur spécifique est déterminé par l'environnement de contexte, tel que:
List <string> Labels = ...
Button :: New équivaut à (x) -> bouton (x), donc le constructeur appelé est: bouton (x);
En plus de créer un seul objet, vous pouvez également créer un tableau d'objets, tels que les deux équivalents suivants:
int [] :: new (x) -> new int [x]
Portée variable
Les expressions LAMBD capturent les variables disponibles dans la portée actuelle, comme:
public void repeatMessage (Text de chaîne, int count) {runnable r = () -> {for (int i = 0; i <count; i ++) {System.out.println (texte); Thread.yield (); }}; nouveau thread (r) .start ();}Mais ces variables doivent être immuables, pourquoi? Voir l'exemple suivant:
int correspond aux correspondances = 0; pour (path p: fichiers) nouveau thread (() -> {if (p a une propriété) correspond ++;}). start (); // illégal pour muter les matchsÉtant donné que les variables mutables ne sont pas des expressions de filetage dans les expressions de lambda, cela est cohérent avec les exigences des classes intérieures, et seules les variables finales définies à l'extérieur peuvent être référencées dans les classes intérieures;
La portée de l'expression de Lambda est la même que celle du bloc de code imbriqué, de sorte que le nom du paramètre ou le nom de la variable dans l'expression LAMBD ne peut pas entrer en conflit avec les variables locales, telles que:
Path first = paths.get ("/ usr / bin"); comparateur <string> comp = (premier, deuxième) -> entier.compare (first.length (), second.length ()); // Erreur: variable d'abord déjà définieSi cette variable est référencée dans une expression de lambda, la référence est cette variable de la méthode qui crée l'expression de lambda, comme:
public class application () {public void Dowork () {runnable runner = () -> {...; System.out.println (this.toString ()); ...}; }} Alors ici, ce.toString () appelle ToString () de l'objet d'application, non exécutable
objets.
Méthode par défaut
Il ne peut y avoir que des méthodes abstraites dans l'interface. Si une nouvelle méthode est ajoutée à une interface existante, toutes les classes d'implémentation de l'interface doivent implémenter cette méthode.
Java 8 introduit le concept de méthode par défaut et ajoute une méthode par défaut à l'interface, qui ne détruira pas les règles d'interface existantes. La classe d'implémentation d'interface peut choisir de remplacer ou hériter directement de la méthode par défaut, telle que:
Personne d'interface {long getID (); String par défaut getName () {return "John Q. public"; }}Java permet un héritage multiple. Comment gérer ce conflit si les méthodes définies dans la classe parent d'une classe sont exactement les mêmes que les méthodes par défaut définies dans l'interface, ou les deux interfaces d'une classe sont exactement les mêmes, comment gérer ce conflit? Les règles de traitement sont les suivantes:
Si la méthode est confrontée à la classe parent et à l'interface: les méthodes de la classe parent prévalent et les méthodes de l'interface doivent être ignorées;
Si la méthode par défaut dans les deux interfaces est confrontée, vous devez remplacer la méthode pour résoudre le conflit;
Méthode statique
Avant Java 8, seules les variables statiques peuvent être définies dans l'interface. À partir de Java 8, des méthodes statiques peuvent être ajoutées à l'interface, comme
L'interface du comparateur a ajouté une série de méthodes statiques pour comparerxxx, telles que:
Public Static <T> Comparator <T> ComparentInt (toIntFunction <? Super T> KeyExtractor) {objets.RequiRenonnull (KeyExtractor); return (Comparator <T> & Serialisable) (C1, C2) -> Integer.Compare (KeyExtractor.ApplyAsint (C1), KeyExtractor.ApplyAsint (C2));}En utilisant cette méthode statique, les deux méthodes suivantes sont également équivalentes:
1 et 1
Arrays.sort (Cities, (First, Second) -> Integer.Compare (First.Length (), Second.Length ()));
2
Arrays.sort (villes, comparateur.compartingInt (string :: longueur));
Par conséquent, lorsque nous concevons nos propres interfaces à l'avenir, nous n'avons plus besoin de définir des classes d'outils distinctes (telles que les collections / collection).
Utilisez simplement la méthode statique dans l'interface.
Classe interne anonyme
Dans le monde Java, les classes intérieures anonymes peuvent implémenter des opérations qui ne peuvent être effectuées qu'une seule fois dans une application. Par exemple, dans une application Android, un événement de clic à bouton est géré. Vous n'avez pas besoin d'écrire une classe distincte pour gérer un événement de clic, vous pouvez le faire avec une classe intérieure anonyme:
Button Button = (Button) findViewById (R.Id.Button1); Button.SetOnClickListener (new onClickListener () {@Override public void onClick (View View) {toast.makeText (mainactivity.this, "Button Clicked", toast.length_short) .show ();}}); Lambda Exemple 1. Runnable Lambda Regardons plusieurs exemples. Voici un exemple de coulable: public void runNableTest () {System.out.println ("=== RunNableTest ==="); // un runnable runnable anonyme r1 = new runnable () {@Override public void run () {System.out.println ("Hello World One!"); }}; // lambda runnable runnable r2 = () -> System.out.println ("Hello World Two!"); // Exécuter deux fonctions d'exécution r1.run (); R2.Run (); } public void runNableTest () {System.out.println ("=== RunNableTest ==="); // un runnable runnable anonyme r1 = new runnable () {@Override public void run () {System.out.println ("Hello World One!"); }}; // lambda runnable runnable r2 = () -> System.out.println ("Hello World Two!"); // Exécuter deux fonctions d'exécution r1.run (); R2.Run (); } Ni l'implémentation ni la valeur de retour n'est renvoyée. Les expressions Lambda Runnable utilisent des blocs de code pour simplifier le code à cinq éléments en une seule instruction. classe publique Personne {chaîne privée donnée; nom de famille privé; Âge privé; genre privé de genre; e-mail de chaîne privée; téléphone de chaîne privé; Adresse de chaîne privée;} classe publique Personne {chaîne privée donnée; nom de famille privé; Âge privé; genre privé de genre; e-mail de chaîne privée; téléphone de chaîne privé; Adresse de chaîne privée;} Ce qui suit est de savoir comment implémenter l'interface du comparateur à l'aide de classes intérieures anonymes et d'expressions Lambda: classe publique ComparatorTest {public static void main (String [] args) {list <ponv> PersonList = personne.createShortList (); // Utiliser la classe intérieure pour implémenter des collections de tri.Sort (PersonList, new Comparator <som> () {public int compare (personne p1, personne p2) {return p1.getSurname (). Compareto (p2.getSurname ());}}); System.out.println ("=== trié ASCNNAME ==="); pour (personne P: PersonList) {p.printName (); } // Implémentation Utilisation de Lambda Expression // Asceming System.out.println ("=== ASC ASC Name ==="); Collection.Sort (PersonList, (Person P1, Person P2) -> p1.getSurname (). Compareto (p2.getSurname ())); pour (personne P: PersonList) {p.printName (); } // DESC Système subséquentiel.out.println ("=== TRONED DESCNAME ==="); Collection.Sort (PersonList, (P1, P2) -> p2.GetSurname (). Compareto (p1.getSurname ())); pour (personne P: PersonList) {p.printName (); }}} classe publique ComparatorTest {public static void main (String [] args) {list <ponv> PersonList = personne.createShortList (); // Utiliser la classe intérieure pour implémenter des collections de tri.Sort (PersonList, new Comparator <som> () {public int compare (personne p1, personne p2) {return p1.getSurname (). Compareto (p2.getSurname ());}}); System.out.println ("=== trié ASCNNAME ==="); pour (personne P: PersonList) {p.printName (); } // Implémentation Utilisation de Lambda Expression // Asceming System.out.println ("=== ASC ASC Name ==="); Collection.Sort (PersonList, (Person P1, Person P2) -> p1.getSurname (). Compareto (p2.getSurname ())); pour (personne P: PersonList) {p.printName (); } // DESC Système subséquentiel.out.println ("=== TRONED DESCNAME ==="); Collection.Sort (PersonList, (P1, P2) -> p2.GetSurname (). Compareto (p1.getSurname ())); pour (personne P: PersonList) {p.printName (); }}} Vous pouvez voir que des classes intérieures anonymes peuvent être mises en œuvre via des expressions Lambda. Notez que la première expression de lambda définit le type du paramètre comme personne; La deuxième expression de lambda omet la définition de type. LAMBDA Expressions Soutient le type de type de type, et si le type requis peut être déduit dans le contexte, la définition de type peut être omise. Ici, puisque nous utilisons les expressions lambda dans un comparateur qui utilise des définitions génériques, le compilateur peut déduire ces deux paramètres en tant que personne.