Les nouveautés du JDK 1.8

Les nouveautés

Les Lambdas, Pourquoi ?

Je vois trois raisons pour que les lambdas soit ajoutés à JAVA :

Lambdas = inner class ?

Les lambdas existe depuis la version 1.1 de java sous la forme d'inner class. Un innerclass permet de définir une classe à l'intérieur d'une autre classe. Ci-dessous un exemple d'inner class:
image 1
La class InnerClass contient une méthode "uneThread" qui renvoie un objet Thread. L'inner class est déclaré entre le "new Thread(" et le ");" de la fin. elle défini l'interface runnable qui contient une méthode run().

Et maintenant, nous allons voir son équivalence en lambda version 1.8:
image 2
Nous pouvons voir que l'on a retiré tout le coté verbeux de l'inner class pour en faire une lambda, le code est considérablement simplifié, et recentré sur l'algorithme métier. La lisibilité est également meilleure.

Une InnerClass peu être construit à partir d'une Interface quelconque, alors que la lambda défini une interface à une méthode.
Une lambda est donc un pointeur sur une Interface ne contenant qu'une seul méthode. L'Interface est alors nommé une "functional interface".
L'InnerClass va creer un object qui lui sera propre. Alors que la lambda ne creer pas d'objet.

Lambdas : Syntaxe

La syntaxe lambda se forme en deux partie, comme illustré ci-dessous:
image 3
La partie à gauche de "->" sont les paramètres envoyés à la méthode. Elle peut prendre zéro (dans ce cas, il faut ouvrir et fermer la parenthèse), un ou plusieurs paramètres (Pour deux ou plus, les parenthèses sont nécéssaires). Lorsqu'il n'y a qu'un seul paramètre les parenthèse ne sont pas obligatoires pour des raisons de lisibilité car 90% des lambdas ont un seul paramètre.
La partie de droite est le code de la méthode. Elle peut s'écrire sur une seul ligne, ou sur plusieurs. Pour le second choix, l'ajout d'accolade est obligatoire.

Lambdas : Types inférés

On peut voir dans l'image ci-dessous une interface I contenant une méthode prenant deux int en paramètres et retournant un int. Et une class A contenant une méthode m qui retourne un I :
image 5b
On peut donc utiliser une lambda. La partie de gauche est les paramètres de la méthode, ici (x, y) correspondent au deux int de la méthode dans l'interface I. La partie de gauche correspond au code redéfini de la méthode dans l'interface I.
L'inférence nous évite d'indiquer le types des paramètres dans la lambda, les types sont directement récupérés depuis l'interface.
On peut noter l'apparition de "@FunctionalInterface" qui permet de signaler au compilateur que l'on utilise une Interface à une seule méthode.

Lambdas : Sémantique

On peut voir sur l'image ci-dessous les différences de sémantique entre une lambda et une InnerClass :

Une lambda peut capturer les valeurs des variables locales à la condition d'être final(comme les InnerClass).
Depuis la version 1.8, Il n'est en revanche pas nécéssaire de mettre le mot clé final. Il suffit juste que la variable ne soit instancé qu'une seul fois pour que le compilateur la considère comme final.
Dans une lambda, le this représente la classe englobante, contrairement aux InnerClass dont le this représente l'objet lui même.

Lambdas : Références de méthodes

Les références de méthode sont une autre façon d'écrire une lambda. Mais au lieu d'écrire le code, on va utiliser une méthode déjà existante. On peut le voir dans cette exemple :
image 10b
Nous avons l'interface I qui contient une méthode qui retourne un int. On peut alors utiliser, pour faire la lambda, la méthode m dan la class B qui dispose de la même signature: () -> b.m() qui peut s'écrire aussi b::m.
On peut également faire référence à des méthodes static.