Cette semaine j'ai rédigé un sujet d'examen sur machine. Dans le code fourni aux élèves (mais qu'ils n'avaient pas à lire) se trouvait :

  final Vecteur[] points = versTableau(listePoints);
  /* ... */
  Arrays.sort(points, 1, points.length,
		     new Comparator<Vecteur>() {
			public int compare(Vecteur p1, Vecteur p2) {
			   return p1.moins(points[0]).det(p2.moins(points[0]));
		       }
		     }); 

En résumé, je fais trier un tableau suivant un certain critère d'ordre, donné par une fonction de comparaison. Comme en Java, on ne peut pas passer de fonctions directement, je suis forcé d'envelopper la fonction dans un objet d'une classe imbriquée anonyme dérivée de l'interface Comparator. Comme on n'a pas le droit depuis une classe imbriquée de référencer une variable locale non final de la fonction enveloppante, j'ai dû rajouter ce mot-clef.

En Objective Caml, je n'aurais eu qu'à bêtement passer une fonction en paramètre : fun x y -> Vecteur.det (Vecteur.moins p1 points.(0)) (Vecteur.moins p2 points.(0)).

Et encore, le code Java ci-dessus, en Java 1.5, est bien plus propre que celui sans types génériques qui aurait convenu pour Java 1.4...

Finalement, les classes imbriquées sont une façon de pallier le manque de types fonctionnels et permettent de créer, au prix de quelques lourdeurs de syntaxe, des clôtures (voir plus loin). D'ailleurs, il est possible de compiler les langages fonctionnels vers des modèles d'exécution orientés objet comme la JVM par cette technique, les classes imbriquées pouvant elles-mêmes être implémentées uniquement à partir des classes de bases.

Comme souvent dans le cas de l'orienté objet, je trouve que l'on introduit des constructions peu lisibles qui masquent la simplicité des concepts que l'on veut programmer. Un comble, sachant que l'orienté objet est censé être une vision de haut niveau, c'est-à-dire proche des besoins du programmeur !

Explication : Je rappelle qu'une clôture est, au niveau machine, une structure de donnée contenant d'une part (enfin, pointant vers) le code exécutable d'une procédure ou fonction, d'autre part certaines données indispensables au fonctionnement de la procédure. Par exemple, si j'écris en Caml, dans un contexte où x est une variable entière, function y -> x + y, le code va être l'opération d'addition, et on lui adjoindra la valeur de x. Un autre exemple : une fonction de comparison de chaînes de caractères pourra devoir être paramétrée par la langue utilisée dans ces chaînes (par exemple, en espagnol, ll est classé après lm, mais pas en français), et donc mettra en jeu trois paramètres : la langue et les deux chaînes à comparer ; mais si on la passe à une fonction de tri, on devra fixer la langue, et le paramètre de langue ira dans la clôture.