La Javase8 publiée en 2013 comprendra un plan appelé Lambda Project, qui est décrit dans le projet JSR-335 en juin de cette année.
JSR-335 a introduit la fermeture en Java. La fermeture existe dans de nombreuses langues populaires, comme C ++, C #. La fermeture nous permet de créer un pointeur de fonction et de les passer comme paramètre. Dans cet article, nous examinerons à peu près les caractéristiques de Java8 et introduirons les expressions Lambda. Et j'essaierai de mettre quelques exemples de programmes pour expliquer certains concepts et grammaire.
Le langage de programmation Java nous fournit le concept de l'interface, et la méthode abstraite peut être définie dans l'interface. L'interface définit l'API et espère que les utilisateurs ou les fournisseurs implémentent ces méthodes. Plusieurs fois, nous ne créons pas de classes d'implémentation indépendantes pour certaines interfaces.
L'utilisation anonyme est largement utilisée. La scène la plus courante utilisée dans la classe interne anonyme est le processeur d'événements. Deuxièmement, les classes internes anonymes sont souvent utilisées dans les programmes multi-thread.
Tout comme nous en discutons, une classe anonyme est la mise en œuvre d'une interface donnée d'un ninja. Habituellement, nous passons l'objet de cette classe d'implémentation en tant que paramètre à une méthode, puis cette méthode appellera la méthode de classe d'implémentation à la classe d'implémentation en interne. Par conséquent, cette interface est appelée une interface de rappel.
Bien que les catégories anonymes soient utilisées partout, elles ont encore de nombreux problèmes. Le premier problème principal est la complexité. Ces classes rendent les niveaux du code en désordre et compliqués, également connus sous le nom de privilège vertical. Deuxièmement, ils ne peuvent pas accéder aux membres non finale de la classe d'emballage. Le mot-clé de cela deviendra très déroutant. Si une classe anonyme a le même nom de membre que sa classe d'emballage, les variables internes couvrent les variables des membres externes. Parce que ce mot-clé est digne d'un objet anonyme lui-même plutôt que de son objet d'encapsulation.
Public void anonymouexample () {String non finalVariable = "non formel exmple"; string variable = "Extérieur Method Variable"; Exécuter la variable de la méthode "; // Ligne inférieure à la ligne. println ("->" + this.variable);}}). La sortie est:
-> Exécuter la méthode varial-> membre de classe Runnable
Cet exemple explique le problème que j'ai mentionné ci-dessus, et l'expression de Lambda résout presque tous les problèmes causés par des classes internes anonymes. Avant d'explorer davantage l'expression de Lambda, jetons un coup d'œil aux interfaces fonctionnelles.
Interfaces fonctionnelles
Les interfaces fonctionnelles sont une interface avec une seule méthode, qui représente ce contrat de méthode.
Un seul dans la définition ci-dessus n'est pas si simple. Je ne comprends pas ce paragraphe.
L'exemple suivant montre clairement comment comprendre le concept d'interfaces fonctionnelles.
L'interface runnable {void run ();} // functionalinterface foo {boolean equals (objet obj);} // non fonctionnel; Fonctionnel; Bar a un Forator de Méthode non objet abusif {Boolean Equals (objet OBJ); non fonctionnel; objet. même signatureLa plupart des interfaces de rappel sont des interfaces fonctionnelles.例如Runnable,Callable,Comparator 等等。以前被称作SAM(Single Abstract Method)
Lambda 表达式
Comme nous l'avons dit, un problème principal de la catégorie anonyme est que le niveau du code semble désordonné, c'est-à-dire que l'expression verticale. Lambda Expression ressemble à une méthode. Ils ont une liste de paramètres formelle et un blocage de ces paramètres.
(String S) -> S.Lengh;
L'exemple ci-dessus signifie que la première expression reçoit une variable de chaîne en tant que paramètre, puis renvoie la longueur de la chaîne. Le second sans paramètres et retour 43. Enfin, le troisième a accepté deux entiers X et Y, et est retourné à la paix.
Après avoir lu beaucoup de mots, je peux enfin donner un exemple de la première expression de Lambda.
Classe publique FirstLambdaExpression {public String variable = "Class Level Variable"; public static void main (String [] arg) {new FirstLambdaExpression () .lambdaExpression ();} public void lambdaExpression () {String variable = "Method Local Variable"; String non finalVariable = "Ceci est une variable non finale"; ; La sortie est:
-> Méthode Variable locale-> Variable de niveau de classe
Vous pouvez comparer la différence entre l'utilisation d'expressions lambda et la classe interne anonyme. Nous pouvons clairement dire que la rédaction de catégories anonymes utilisant l'expression de Lambda résout le problème de la visibilité variable. Vous pouvez jeter un œil aux annotations dans le code.
La syntaxe d'expression General Lambda comprend une liste de paramètres, le mot-clé Arrow "->" est le corps principal. Le sujet peut être une expression (instruction One -line) ou une phrase multi-lignes. S'il s'agit d'une expression, il sera calculé et renvoyé. La rupture et la poursuite ne peuvent être utilisées qu'à l'intérieur du cycle.
Pourquoi choisir cette forme de grammaire spéciale, car actuellement ce style est généralement en C # et Scala, qui est également une écriture générale de l'expression de lambda. Cette conception de grammaire résout essentiellement la complexité du type anonyme. Mais en même temps, il est également très flexible. Le résultat de l'expression est comme sa propre valeur de retour. Cette flexibilité peut garder le code simple.
L'expression de lambda est utilisée comme anonyme, de sorte qu'elles peuvent être utilisées de manière flexible dans d'autres modules ou d'autres expressions de lambda (expressions de lambda imbriquées).
// Lambda Exposition est enfermée avec des méthodes Blocs de paramètre.//Target Le type d'interface est le type de paramètre de méthodes. () -> {System.out.println ("Running dans différents threads");});
Si vous examinez de plus près l'expression de Lambda, vous verrez que le type d'interface cible ne fait pas partie d'une expression. Le compilateur aide à déduire le type et l'environnement environnant de l'expression de lambda.
LAMBDA L'expression doit avoir un type de cible, et ils peuvent s'adapter à n'importe quel type de cible possible. Lorsque le type cible est une interface, les conditions suivantes doivent être remplies pour compiler correctement:
Étant donné que le compilateur peut connaître le type et le nombre de paramètres via l'instruction de type cible, dans l'expression de Lambda, la déclaration de type de paramètre peut être omise.
Comparator c = (s1, s2) -> s1.compareToIgnoreCase(s2);
De plus, si la méthode déclarée dans le type cible ne reçoit qu'un seul paramètre (plusieurs fois, c'est le cas), les petits supports du paramètre peuvent également être écrits, par exemple:
ActionListenr écouterr = event-> event.getWhen ();
Une question très évidente survient, pourquoi Lambda exprime-t-elle un nom de méthode spécifié?
La réponse est: l'expression de Lambda ne peut être utilisée que pour l'interface fonctionnelle, tandis que l'interface fonctionnelle n'a qu'une seule méthode.
Lorsque nous déterminons une interface fonctionnelle pour créer des expressions de lambda, le compilateur peut percevoir la signature de la loi chinoise de l'interface fonctionnelle et vérifier si l'expression donnée correspond.
Cette grammaire flexible nous aide à éviter d'utiliser un privilège vertical anonyme, et il n'apportera pas de privilège horizontal (très longue phrase d'une seule voie).
La grammaire d'expression de Lambda est liée au contexte, mais ce ne sont pas la première fois. Les opérateurs de diamants ajoutés par Java SE 7 ont également ce concept, qui est déduit par le contexte.
void invoke (runnable r) {r.run ()} void future invoke (callable r) {return c.compute ()} // ci-dessus sont deux méthodes d'interface onal s = invoke (() -> "Done"); / Quelle invocation sera appelée?
La réponse à la question ci-dessus est d'appeler la méthode de réception du paramètre appelable. Dans ce cas, le compilateur sera résolu par la charge de différents types de paramètres. Lorsqu'il existe plusieurs méthodes de chargement applicables, le compilateur vérifie également la compatibilité de l'expression de lambda et le type de cible correspondant. En termes simples, la méthode invoquée ci-dessus devrait revenir, mais une seule méthode invoquée a une valeur de retour.
Les expressions de Lambda peuvent être explicitement converties en types de cibles spécifiés, tant qu'ils sont compatibles avec les types correspondants. En regardant le programme suivant, j'ai implémenté trois types de calculables et ils l'ont tous converti en type de classe.
Classe publique FirstwithLambdaExpressions {public static void main (String [] args) {listl = araySList (callial) -> "callable 1", (appel ABS) () -> "callable 2", (callable) (callable)) -> "Calable 3"); ) {e1.printStackTrace ();} e.shutdown ();} public void Dumplist (list list) lève InterupTexception, ExecutionExcepti on {for (futur future: list) {System.out.println (future.get ()); }}}Comme nous en avons discuté précédemment, les catégories anonymes ne peuvent pas accéder aux variables non finales dans l'environnement environnant. Mais il n'y a pas une telle limite dans l'expression de Lambda.
À l'heure actuelle, les interfaces fonctionnelles définies ne sont applicables qu'à l'interface. J'ai essayé de créer une expression lambda d'une seule méthode abstraite, mais une erreur de compilation a été commise. Selon JSR -335, la future version de l'expression de Lambda peut prendre en charge les classes fonctionnelles.
Citation de la méthode
Méthodes référencées comme une méthode de référence sans l'appeler.
Lambda Expression nous permet de définir une méthode anonyme et de l'utiliser comme instance de l'interface fonctionnelle. Les méthodes sont très similaires à l'expression de Lambda.
Système :: GetProperty "ABC" :: PlayString :: SpécifieSuper :: toStringArrayList :: Nouveau
L'instruction ci-dessus montre la syntaxe générale de la méthode et la référence au constructeur. Ici, nous avons vu un nouveau personnage d'exploitation ":::: Double Colon). Je ne connais pas le nom exact comme cet opérateur, mais JSR le fait référence en tant que séparateurs, et la page Wikipedia le fait référence comme une opération d'analyse de la portée .
La référence cible ou le récepteur est placée derrière le fournisseur et les séparateurs. Cela forme une expression qui peut citer une méthode. Dans l'instruction finale, le nom de la méthode est "nouveau". Cette expression cite la méthode de structure de la classe ArrayList (la référence au constructeur dans la section suivante)
Avant de comprendre cela, je veux vous laisser voir la force de la méthode citée.
Importer Java.util.arry; = {Nouvel employé ("Nick"), nouvel employé ("Robin"), nouvel employé ("Josh"), nouvel employé ("Andy"), nouvel employé ("Mark")}; Avant le tri: "); DumpEmployee (employés); arrays.sort (employés, employeee :: myCompare); System.out.println (" après le tri: "); Mloyees);] employés) {for (Employee Emp: Arrays. aslist (employés)) {System.out.print (emp.name + ",");} System.out.println (); (EmployeeE EMP1, Employee EMP2) {return EMP1.NAME ..compareto (emp2.Name);}} La sortie est:
Avant le tri: Nick, Robin, Josh, Andy, Mark, après tri: Andy, Josh, Mark, Nick, Robin,
La sortie n'est pas spéciale. Méthode statique MyCompare reçoit deux objets employés et renvoie leurs noms à comparer.
Dans la méthode principale, j'ai créé un tableau différent d'un employé et je l'ai transmis à la méthode Arrays.Sort pour le référencer à la gamme d'expression (Employee :: MyCompare).
Attendez une minute, si nous regardons Javadoc, vous constaterez que le deuxième paramètre de la méthode de tri est le type corarateur, mais nous passons la référence de méthode statique de l'employé. Le problème important est ici.
Jetons un coup d'œil à pourquoi. La méthode Arrays.Sort s'attend à une instance d'un comparateur, et ce comparateur est une interface fonctionnelle, ce qui signifie qu'il n'a qu'une seule méthode, c'est-à-dire comparer. Ici, nous passons également avec malveillance sur une expression de lambda, qui fournit la mise en œuvre de la méthode Compaare dans cette expression. Mais en nous, notre classe d'employés a déjà une méthode de comparaison. C'est juste que leurs noms sont différents.
Lorsqu'il existe plusieurs méthodes du même nom, le compilateur choisira la meilleure correspondance en fonction du type cible. Pour comprendre, regardez un exemple:
Public static int myCompare (employeee EMP1, employee EMP2) {return emp1.name.compareto (emp2.Name);} // une autre méthode avec le même nom que de la. {{) {{) {{Return int1.compareto (int2);} J'ai créé deux tableaux différents pour le tri.
Employé [] employés = {nouvel employé ("Nick"), nouvel employé ("Robin"), nouvel employé ("Josh"), nouvel employé ("Andy"), nouvel employé ("Mark")}; ins = {1, 4, 8, 2, 3, 8, 6}; Maintenant, j'exécute les deux lignes de code suivantes
Arrays.sort (employés, employé :: myCompare);
Ici, la méthode des références dans les deux lignes de code est la même (Employee :: MyCompare).
Ne soyez pas induit en erreur par la méthode statique, nous pouvons également créer une référence à l'exemple de méthode. Pour les méthodes statiques, nous utilisons les noms de classe :: Nom de la méthode pour écrire la référence de la méthode.
L'exemple ci-dessus est assez bon, mais nous n'avons pas à rédiger une méthode pour la comparaison d'Integer, car Integer a implémenté comparable et fournit une méthode de mise en œuvre compareto.所以我们直接使用下面这一行就行了:
Arrays.sort (INTS, Integer :: Compareto);
Voir cela, vous sentez-vous un peu confus? Non? Alors permettez-moi de vous confondre ici. La méthode des membres est référencée :: Ce devrait être un objet avant, mais pourquoi la phrase ici est en effet légitime.
La réponse est: ce type d'instruction permet d'utiliser dans certains types spécifiques. Integer est un type de données, et pour le type de données, cette instruction est autorisée.
Si nous transformons la méthode des employés MyCompare en non-statique, alors utilisez: Employee :: myCompare, il y aura des erreurs de compilation: aucune méthode appropriée trouvée.
Référence de méthode constructive
Les références de constructeur sont utilisées comme une classe qui fait référence à un constructeur sans institutionnalisation spécifiée.
La référence de méthode constructive est une nouvelle caractéristique de Javase 8. Nous pouvons construire une référence à une méthode constructive et la transmettre en tant que paramètre au type cible.
Lorsque nous l'utilisons pour référencer, nous citons une méthode existante pour les utiliser. De même, lorsque nous utilisons la référence de méthode constructive, nous créons une référence aux méthodes constructives existantes.
Dans la section précédente, nous avons vu le nom de grammaire référencé par le constructeur :: Nouveau, qui ressemble à une référence de méthode. La référence à cette méthode construite peut être attribuée aux instances des interfaces fonctionnelles cibles. Il peut y avoir plusieurs constructeurs dans une classe.
Pour moi, il est difficile d'écrire la première méthode constructive. En fin de compte, j'ai passé beaucoup de temps à travailler dur, et enfin "Ah, j'ai trouvé ...", regardez la procédure suivante.
Classe publique ConstructorReferences {public static void main (string [] ar) {myInterface dans = myClass :: new; } La sortie est:
-> com.myclass@34e5307e
Cela semble un peu étonnant, non?
Cet exemple a suscité un autre problème dans mon cœur: comment instancier une méthode constructive avec un paramètre? Regardez la procédure ci-dessous:
Classe publique ConstructorReferences {public static void main (String [] ar) {EmlPoyEproder Provider = Employee :: New; ; Âge) {this.name = name; La sortie est:
-> Nom de l'employé: John-> Employé: 30 ans
Avant de lire cet article, jetons un coup d'œil à la fonction la plus cool de la méthode Javase8-Default
Méthodes par défaut
Javase8 introduira un concept appelé la méthode par défaut. La première version Java de l'interface a une interface très stricte. Dans la prochaine version Java, l'implémentation par défaut de la méthode dans l'interface est autorisée. Pas beaucoup de bêtises, regardez ce qui suit:
Classe publique defaultMethods {public static void main (String [] ar) {normalInterface instance = new NormalInterfaceIml (); instance.mynormalthod (); System.out.println ("-> MyDefaultMethod");}} classe normalInterfaceIMPl ImplemememeRinterface {@Override public void mynormalthod () {) System.out.println ("-> mynormalthod");}} La sortie est:
-> MydefaultMethod
L'interface ci-dessus déclare deux méthodes, mais la classe d'implémentation de cette interface ne réalise que l'une d'entre elles, car MyDefaultMethod utilise les modificateurs par défaut, et il fournit un bloc de méthode pour l'implémentation par défaut. Les règles de charge lourde de GM prennent toujours effet ici. Si la classe d'implémentation implémente la méthode dans l'interface, ce sera la méthode de la classe d'appels lorsque l'appel, sinon, l'implémentation par défaut sera appelée.
L'interface de l'interface parent intégrée peut augmenter, modifier et supprimer l'implémentation par défaut de l'interface parent.
Interface ParentInterface {void IciallyNomal (); void initialementdefault () default {System.out.println ("- >> /> MyDefaultMetMethod"); ce -> InitialementNomal ");} void InitialementDefault (); // maintenant une méthode normale} Dans cet exemple, ParentInterface définit deux méthodes, l'une est normale et l'autre est implémentée par défaut.
Imaginez qu'une classe a hérité de la classe C, a réalisé l'interface I, et C avait une méthode, et la méthode qui a fourni la méthode par défaut dans laquelle a fourni la méthode par défaut était compatible. Dans ce cas, la méthode en C donnera la priorité à la méthode par défaut dans i, et même la méthode en C est toujours prioritaire lorsque la méthode est abstraite.
Classe publique defaultMethods {public static void main (String [] ar) {interfaxe impl = new NormalInterfaceImpl (); ;}} interface interfaxe {public void defaultMethod () default {System.out.println ("-> interfaxe"); La sortie est:
-> Parentclass
Le deuxième exemple est que ma classe a implémenté deux interfaces différentes, mais les deux interfaces fournissent la même instruction avec la même méthode d'implémentation par défaut. Dans ce cas, le compilateur ne sera pas en mesure de comprendre ce qui se passe. Cela peut être fait de la manière suivante.
Classe publique defaultMethods {public static void main (String [] ar) {FirstInterface Imp = new NormalInterFampl (); );}} interface secondInterface {public void defaultMethod () default {system.out.println ("-> secondInterface"); La sortie est:
-> secondInterface
Maintenant, nous avons lu l'introduction de la fermeture Java. Dans cet article, nous sommes entrés en contact avec les interfaces fonctionnelles et la fermeture de Java, qui comprenaient l'expression de Lambda de Java, les références de méthode et les références de constructeur. Et nous avons également écrit un exemple de Hello World de l'expression de Lambda.
Javase8 arrive bientôt.