23 janvier 2014

Les détails sont dans l'épaisseur du trait

Olivier Croisier a publié un article sur ses expérimentations avec Java 8, plus précisément l'introduction des default methods dans les interfaces. Cette nouveauté dans la syntaxe Java a été introduite initialement pour permettre à l'API Collections d'évoluer sans nécessiter à tout un écosystème de librairie de produire de nouvelles versions mises à jour pour Java 8, autrement dit d'attendre 2020 pour que ce soit utilisable concrètement.

Comme l'a très bien analysé Olivier, ces default methods apportent à Java le concept de trait très utilisé en Scala. La tournure fonctionnelle que Java 8 va permettre de faire prendre au langage est un pas majeur, bien plus important amha que l'introduction des génériques en Java 5.

Pour rebondir sur le problème du diamant exposé par Olivier, mon interrogation sur les conflits de méthodes introduites pas les default methods se pose un peu différemment :

nous avons donc une classe Clazz qui implémente deux interfaces Foo et Bar.

1 public interface Foo {
2     public default void helloWorld() {
3         System.out.println("Hello World");}
4 }

1 public interface Bar {
2 }


1 public class Clazz implements Foo, Bar {
2 
3     public static void main(String args[]) throws Exception {
4         new Main().helloWorld();
5     }
6 }

Disons que la première déclare une default method helloworld(). Si Bar définit lui aussi la même default method le compilateur détecte le conflit. Supposons cependant que dans un premier temps Bar n'a pas de telle méthode et que nous compilons notre projet. Nous ajoutons alors a Bar la default method conflictuelle, et re-compilons la classe Bar (et elle seule).

Question : est-ce que notre programme continue de fonctionner comme avant - en gros, est-ce que le bytecode produit fait directement référence à la méthode Foo.helloworld(). Ce qui veut dire que nous ne pouvons plus le re-compiler depuis les sources. Une bonne raison de configurer votre intégration continue avec un "mvn clean install" :)

Je vous laisse essayer par vous même pour avoir la réponse :-D

Java 8 apporte des changements profonds, et expérimenter est la seule façon concrète de comprendre leur impact. Voir ce que fait Yan sur https://github.com/ybonnel/Jdk8Experiments par exemple, je vous encourage à en faire autant.

Je suis en ce moment (exactement en ce moment, dans le TGV Rennes-Paris, connecté en tethering) en train de lire le livre "Functional Programming for Java Developers". Si le livre n'est pas tout juste sorti des presses et ne parle donc qu'à peine de Java 8, et avec une syntaxe obsolète, les concepts fondamentaux sont bien présents et très utiles.


J'entend beaucoup de monde me dire qu'ils n'utiliseront pas Java 8 avant une décennie, bloqués par les contraintes client d'un runtime websphere 6. C'est évidement une réalité, et clairement vous n'aurez pas de traits dans vos applications. Ca n'empêche pas de commence à appréhender les concepts fonctionnels, qui sont un excellent complément à l'approche objet.

En dehors de l'approche fonctionnel que va (mieux) permettre Java 8, ces changements annoncent aussi des bouleversements dans l'écosystème des librairies et frameworks. Exemple type, de nombres frameworks définissent des classes abstraites, que votre code doit étendre. Un jeu d'interface serait plus propre, mais alors impossible de faire évoluer les API. Jenkins par exemple comporte une quantité phénoménale de classes abstraites pour cette raison.

Autre approche si on aime pas les classes abstraites, faire à la façon d'Eclipse, en définissant des interfaces HelloWorld1, HelloWorld2, HelloWorld3, et en testant au runtime quelle interface est implémentée, bref une solution pire que le mal lui même.

Bref, tous les frameworks pre-java 8 - en dehors d'Eclipse - se basent sur des classes abstraites, et pourrissent nos arbres d'héritage. Une opportunité apparaît donc avec Java 8 pour une nouvelle génération de frameworks, basés sur des traits. Le même genre de changement qui a laissé commons-collections sur le carreau avec l'arrivée des génériques et une mise à niveau jamais aboutie, qui a laissé la place à Guava et quelques autres.

Donc, oui, votre client a du Java 5 en production. Oui, c'est une version "End of Life" mais il s'en fout, et vous n'y pouvez rien. Et pourtant, si vous ne voulez pas passer pour un développeur avec des faux airs de Cobol dans quelques années, il est temps pour vous de regarder ce qu'apporte Java 8, d'expérimenter, de découvrir les bénéfices et les concepts de la programmation fonctionnelle, et de pleurer en revenant à votre code sans lambdas ;)