Lambda! Rémi Forax Univ Paris-Est Marne-la-Vallée forax at univ-mlv dot fr - ParisJUG Java.next() - Mars 2012
What Else?
Lambda == Inner class? Java a des lambdas depuis la version 1.1 Exemple utilisant Guava import com.google.common.collect.collections2; class Person { String lastname; public Iterable<Person> homonyms(list<person> persons) { return Collections2.filter(persons, new Predicate<Person>() { public boolean apply(person p) { return lastname.equals(p.lastname); );
Lambda == Fonction anonyme Ne garder que la partie codante! import com.google.common.collect.collections2; class Person { String lastname; public Iterable<Person> homonyms(list<person> persons) { return Collections2.filter(persons, new Predicate<Person>() { public boolean apply(person p) { return lastname.equals(p.lastname); );
Syntaxe Celle de C#/Scala, mais avec -> au lieu de => () > 2 () > { System.out.println("Lambda!"); x > x + 2 (x, y) > x + y Les types des paramètres sont specifiés ou inferrés (int x, int y) > x + y
Type d'un lambda Ajouter un type de fonction genre: *(int comparator(int, int)) Problèmes: Théorique: type structurel pas nominal Relation avec *(long foo(byte, byte))? Pas une bonne idée (Java <> Scala) Pratique: il faut modifier le code des APIs Ré-écrire Guava, etc Pas une bonne idée (Java <> Kotlin)
In SAM, we trust Single Abstract Method, interface à méthode unique FileFilter filter = file > file.isdirectory(); Callable<Integer> callable = () > { return fib(17); ; Predicate<Person> predicate = p > p.lastname!= null; Le type des paramètres n'est pas inferré à partir du corps de la lambda mais en fonction du type des paramètres de la méthode du SAM
Method Reference Une référence à une méthode est File[] files = dir.listfiles(file::isdirectory()); au lieu de File[] files = dir.listfiles(file > file.isdirectory()); ce qui évite de créer une fonction anonyme si une méthode existe déjà
Sémantique Une lambda peut capturer les valeurs des variables du scope Callable<Integer> addoperation(int x, int y) { return () > x + y; les variables doivent être final ou effectively final class Person { String lastname; public Iterable<Person> homonyms(list<person> persons) { return C2.filter(persons, p > p.lastname.equals(this.lastname)); this est l'instance courante de la classe, pas la lambda!
Lambda!= inner class class Person { String lastname; public Iterable<Person> homonyms(list<person> persons) { return C2.filter(persons, p > p.lastname.equals(this.lastname)); Trouver le bug! class Person { String lastname; public Iterable<Person> homonyms(list<person> persons) { return C2.filter(persons, new Predicate<Person>() { public boolean accept(person p) { return p.lastname.equals(this.lastname)); ;
Lambda & collections Les lambdas sans support au niveau des collections, ça ne sert pas à grand chose Problème java.util utilise des interfaces, impossible d'ajouter des méthodes Solutions Créer des List2, Set2, etc Non! Faire des imports statique à la C# Non! Mettre du code dans les interfaces Euh...
Default methods Une interface peut contenir des default methods donc du code interface Iterable<E> { public Iterator<E> iterator(); public Iterable<E> filter(predicate<e> predicate) default { // code ici Ajouter une default method est compatible binairement! Les default methods sont des traits ou mixins simplifiés pas de champ!
Sémantique Héritage d'abord sinon le sous-type le + précis sinon erreur! interface I { void m() default {... class A { void m() {... class B extends A implements I { // A::m()
Sémantique Héritage d'abord sinon le sous-type le + précis sinon erreur! interface I { void m() default {... interface J extends I { void m() default {... class C implements I, J { // J::m()
Sémantique Héritage d'abord sinon le sous-type le + précis sinon erreur! interface I { void m() default {... interface J { void m() default {... class C implements I, J { // fail or // void m() { return I.super.m();
Strategies d'implantations Eviter le piège du 'tout est fait par le compilateur'
Lambda constante Une lambda qui ne capture pas de valeur du scope doit être constante FileFilter filter = file > file.isdirectory(); Traduction possible: FileFilter filter = LAMBDA$1; private static final FileFilter LAMBDA$1 = new FileFilter() { public boolean accept(file file) { return file.isdirectory(); ; mais la lambda est crée trop tôt!
Invokedynamic + Lambda BSM On utilise invokedynamic pour retarder la création à l'exécution FileFilter filter = invokedynamic lambda () BSM: Lambdas.lambdaBSM [#lambda1(file)boolean, FileFilter#accept(File)]... private static boolean lambda$1(file file) { return file.isdirectory(); La conversion vers un SAM prend en paramètre le code de la lambda et la méthode du SAM à implanter package java.lang; public class Lambdas { CallSite lambdabsm(lookup lookup, String name, MethodType methodtype, MethodHandle lambdacode, MethodHandle sammethod) { if (methodtype.parametercount() == 0) { return new ConstantCallSite(MHs.constant(...));...
Lambda BSM 1. recoit le code de la lambda comme un method handle 2. Adapt le code (boxing,...) pour matcher la signature de la méthode du SAM 3. Si le method handle est constant créer un dynamic proxy que l'on stock dans un CallSite constant 4. renvoie un method handle qui prend les arguments et a créer un dynamic proxy à chaque appel
Proxies Proxy lors de la compilation Occupe de la place sur disque La classe doit être chargée et vérifiée Proxy à l'exécution Généré à la demande Pas de chargement ni de vérification donc les proxies des lambda vont être générés à l'exécution
Proxies à l'exécution Strategie 1 (conservatrice) Créer un proxy correspondant au code d'une inner class, les valeurs du scope sont stockées dans des champs 1 proxy par site d'appel (callsite) cons: trop de classes, conso memoire, empèche certaines optimisations pro: marche comme les inner classes Strategie 2 (JSR 292) 1 proxy par SAM Créer un proxy qui stock le method handle dans un champ cons: actuellement, 2x plus lent que la strategie 1 pro: l'implantation de la JSR 292 peut être optimisé pour ce cas
Reste un problème de perf... filter() va être appelé avec plein de lambdas différentes risque de polution du profile La compilation par tiers peut résoudre ce problème! public Iterable<Person> homonyms(list<person> persons) { return persons.filter( invokedynamic(...) [lambda$1, Predicate#accept] ); private static boolean lambda$1(person p) { return p.lastname.equals(this.lastname)); interface Iterable<T> { public Iterable<Person> filter(predicate<? super T> predicate) default { Iterator<E> it = iterator(); return () > return new Iterator<T>() { public boolean hasnext() {... predicate.accept(it.next()) // accept() megamorphique => pas d'inline :(... ;
TL;DR Lamba dans Java 8 (enfin!) Bien intégré dans le JDK Bien compris par la VM Implantation dispo ici http://hg.openjdk.java.net/lambda/lambda Merge avec le workspace du jdk8 prévu en Avril, si tout ce passe bien :)
Lambda by ~kinglou88 DeviantArt 2009-2012 Nespresso What Else? nespresso.com Ark of covenant http://en.wikipedia.org/wiki/ark_of_the_covenant One dollar bill (reverse) http://en.wikipedia.org/wiki/united_states_one-dollar_bill I am not crazy by *jump-button DeviantArt 2008-2012