Connaissez-vous lambdaj ?
C’est une librairie Java open-source développée par Mario Fusco, qui permet de manipuler plus simplement les collections et d’effectuer des itérations avec une approche fonctionnelle. Plutôt qu’un long discours, pour vous montrer son intérêt voici un exemple tiré du site de LambdaJ :
import static ch.lambdaj.Lambda.*; public class MyClass { // .... // some code public static void testLambdaj() { List<Person> personInFamily = asList(new Person("Domenico"), new Person("Mario"), new Person("Irma")); forEach(personInFamily).setLastName("Fusco"); } }
La première ligne permet de créer simplement une liste de personne. La méthode asList de la librairie LambdaJ permet de créer une liste de Person. C’est le pattern static factory expliqué dans le livre de Joshua Bloch, Effective Java 2nd edition. Si vous utilisez Google Guava comme moi, cela doit vous parler.
La deuxième ligne est vraiment ce qui fait l’intérêt de lambdaJ je pense. Elle permet d’itérer la collection et de donner le même nom de famille à chacune des personnes de la liste. LambdaJ permet de remplacer vos boucles for par expression lambda, d’où le nom de la librairie. Les fonctions lambdas regroupent les closures, mais pas seulement. Avant de me donner une leçon de programmation fonctionnelle, lisez la définition sur Wikipedia. Attention à ne pas confondre une classe anonyme, ce que Java sait faire, et une fonction anonyme, un concept mathématique de 1936 que Java ne sait pas encore faire, contrairement à C#, Python ou JavaScript.
Techniquement, la librairie utilise un proxy Java sur la collection qui implémente l’interface Iterable ainsi que toutes les méthodes publiques de l’élément de la collection, ce qui permet d’appeler setLastName ici par exemple. LambdaJ se charge de propager votre appel à chacun des éléments de la collection pour vous. Le surcoût en terme de performance est d’un facteur 3 en moyenne. Mario a d’ailleurs réalisé un ensemble de benchmarks afin de montrer l’impact.
D’autes exemples ?
Soit le modèle suivant proposé comme exemple sur le site de lambdaj :
Comment itérer la liste des voitures et afficher l’ensemble des marques dans une seule String ?
L’approche classique :
StringBuilder sb = new StringBuilder(); for (Car car : db.getCars()) { sb.append(car.getBrand()).append(", "); } String brands = sb.toString().substring(0, sb.length()-2); System.out.printl(brands); // Ferrari, Renault, Mercedes, Lada
Voici l’approche simple de lambdaJ :
String brands = joinFrom(db.getCars()).getBrand(); System.out.printl(brands); // pareil mon bon monsieur...
Retrouver la voiture la plus vendue ?
Voici comment retrouver la voiture qui a été vendue le plus de fois avec une approche classique :
Map<Car, Integer> carsBought = new HashMap<Car, Integer>(); for (Sale sale : sales) { Car car = sale.getCar(); Integer boughtTimes = carsBought.get(car); carsBought.put(car, boughtTimes == null ? 1 : boughtTimes+1); } Car mostBoughtCarIterative = null; int boughtTimesIterative = 0; for (Entry<Car, Integer> entry : carsBought.entrySet()) { if (entry.getValue() > boughtTimesIterative) { mostBoughtCarIterative = entry.getKey(); boughtTimesIterative = entry.getValue(); } }
L’implémentation avec lambdaJ est plus expressive pour le même résultat :
Groupgroup = selectMax( group( sales, by( on(Sale.class).getCar() ) ).subgroups(), on(Group.class).getSize() ); Car mostBoughtCar = group.findAll().get(0).getCar(); int boughtTimes = group.getSize();
Cas d’usages
Il serait un peu étonnant de voir dans un DAO ou une Entité ce code avec lambdj non ? En effet vous allez me dire, si j’accède à une base de données, mon modèle relationnel me permet d’exprimer en SQL ce que nous voyons dans ces exemples. Si vous utilisez une base non relationnelle type BigTable sur Google App Engine, souvez-vous qu’il n’est pas possible de faire des jointures, d’utiliser des expressions relationnelles. Je pense que lambdaJ peut permettre d’exprimer le métier de votre modèle de manière plus lisible qu’un paquet de boucles for non ?
J’ai pris le code de l’eXpress-Board et grâce à lambdaJ j’ai remplacé du code un peu dur à relire par des expressions simples. Faites ce test : cherchez des boucles for par exemple dans votre projet, regardez ce que cela donnerait avec lambdaJ, vous verrez que c’est sympa.
Retenez que LambdaJ est un DSL qui permet d’itérer sur des Collections avec une approche pseud-fonctionnelle, tout en conservant un typage fort.
Rendez-vous en octobre à la conférence Soft-Shake pour assister à une présentation de lambdaJ par Mario, ou sinon à JavaOne la semaine du 20 septembre.
– Le site lambdaj : http://code.google.com/p/lambdaj/
– La présentation au Geneva JUG de LambdaJ http://www.slideshare.net/GenevaJUG/lambdaj-at-genevajug
En fait Mario fera une présentation de Scala à SoftShake, pas de LambdaJ.
Mais il se fera un plaisir de vous en parler entre 2 sessions !
Merci Nicolas, je cherchais un truc dans ce goût là. C’est le petit bout qui manque à Guava
Très bon à savoir. C’est, je trouve, assez proche de ce qu’on peut faire avec LINQ en .NET, qui je trouve, est une invention géniale !
Franchement je suis vraiment, vraiment, vraiment pas fan du « pattern static factory » (çà fait quand même un peu procédural tout çà). Déjà que je râle souvent quand je vois des gens me faire des classes avec que des méthodes statiques (hors helpers) et tout en paramètre à chaque fois sur chaque méthode, mais là avec la possibilité de faire des méthodes « libres » juste importées par des « import static », c’est la porte ouverte à tout et n’importe quoi. Berk.
Intéressant, je ne connaissais pas cette lib.
Cependant, je préfère quand même l’approche de op4j qui a l’avantage ne garder une syntaxe plus claire sans devoir polluer le namespace avec un import static.
Je la garde quand même sous le coude, ca peux servir un jour.
Très intéressant, merci 🙂
Je vais essayer ça dans un de mes projets, ça peut être intéressant.
Juste par curiosité, c’est vraiment utilisé dans l’ExpressBoard, ou c’était juste des essais ?
Merci
Baptiste
Intéressant, mais une perte de performance x3, on parle juste de la perf liée à l’itération, pas la perf globale du traitement je suppose?
Ne serait il pas mieux de faire cela directement en Scala. Le langage est je pense plus propice pour cela et on ne devrait pas avoir le x3 au final non ?
@GIS If you don’t like static methods maybe you could give a glance to the lambdaj fluent interface collections.
http://code.google.com/p/lambdaj/wiki/LambdaCollections
It allows you to use all the lambdaj feature, but possibly in a more object oriented (and maybe also more readable) way.
@Seb I am a big Scala fan, as my next talk about Scala at soft-shake should demonstrate. I never thought never said that lambdaj could be in any way a replacement for Scala. If you have the possibility to use Scala, this is a no brain choice: go for Scala and forget lambdaj. Lambdaj is only for who (like sometimes myself) would like to manipulate collections in a more functional way but are not allowed to develop in Scala.
Pour ceux qui se posent la question, lambdaj utilise cglib et pas (que)
java.lang.reflect.Proxy
(qui ne sait gérer que des interfaces).