Published by

Il y a 11 ans -

Temps de lecture 4 minutes

Solution du Quizz Java 8

La semaine dernière, nous avons proposé sur ce blog un quizz spécifique à Java 8. Au vu du comportement prévu pour Java 8 actuellement, la bonne réponse à ce quizz est :

x2
x2

Retrouvez toutes les explications dans la suite du billet :

  • OpenJdk 8 : comportement par défaut
  • Lambda, invokedynamic et metafactory

OpenJdk 8 : comportement par défaut

Pour le moment Java 8 n’est pas disponible. Seul l’OpenJDK 8 en cours de développement permet d’observer ce que sera Java 8. À l’heure où ces lignes sont écrites (nous utilisons ici l’OpenJDK 8 24.0-b07), l’OpenJDK 8 affiche par défaut :

x1
x2

En effet, par défaut, la lambda expression () -> { System.out.println(« X »); } est directement traduite par une inner classe anonyme. Pour rappel, nous avions une méthode makeX :

private static X makeX() {
    return () -> { System.out.println("X"); };
}

Par défaut, elle est traduite en :

private static X makeX() {
    return new X() {
         @Override
         public void run() {
              System.out.println("X");
         }
    }
}

On peut l’observer en affichant directement x1.toString() et x2.toString() :

YourTestClass$1@30ae8764
YourTestClass$1@123acf34

Chaque appel à makeX génère donc une nouvelle instance d’une classe anonyme. Si vous regardez le répertoire contenant les binaires Java (ie. *.class), le compilateur vous a généré le fichier YourTestClass$1.class.

Lambda, invokedynamic et metafactory

Toutefois, dans l’OpenJDK 8, le compilateur javac propose l’option -XDlambdaToMethod. Cette option vous permet d’activer le comportement à terme de la traduction des lambdas expressions. En activant cette option et en relançant le programme, nous obtenons la bonne réponse du Quizz. Si nous nous amusons à afficher directement x1.toString() et x2.toString(), nous obtenons :

YourTestClass$$Lambda$1@5506d4ea
YourTestClass$$Lambda$1@5506d4ea

Chaque appel à makeX semble fournir un singleton provenant d’une même inner classe anonyme étrangement nommée. Néanmoins, si vous regardez le répertoire de destination des binaires Java (en ayant soigneusement supprimé tous les fichiers *.class avant d’effectuer la compilation), vous ne trouverez pas de fichier YourTestClass$$Lambda$1.class dans vos répertoires.

Compilation

La traduction de la lambda expression n’est pas vraiment faite au moment de la compilation. En réalité, elle est répartie entre la phase de compilation et le runtime. Le compilateur va placer au niveau de la lambda expression une instruction qui a été récemment ajoutée à la JVM : la fameuse instruction invokedynamic de la JSR292. Cette instruction est accompagnée de toutes les méta-informations nécessaires à la traduction de la lambda expression au runtime. Cela inclut le nom de la méthode à appeler, ses types d’entrée et de sortie, ainsi qu’une méthode appelée bootstrap. Le bootstrap a pour rôle de définir l’instance sur laquelle la méthode sera appelée, lorsque la JVM exécute l’instruction invokedynamic. Dans la cadre d’une lambda expression, Java fait appel à une méthode de bootstrap particulière appelé lambda metafactory.

Pour en revenir à notre Quizz, le corps de la lambda expression est converti en méthode privée static. Ainsi, () -> { System.out.println(« X »); } est convertie dans YourTestClass en :

private static void lambda$0() {
    System.out.println("X");
}

Vous pouvez l’observer si vous utilisez le décompilateur javap (fournit avec le JDK) avec l’option -private. Quand à la méthode makeX, en lieu et place de la lambda expression, vous trouverez l’instruction invokedynamic avec notamment la référence sur un constructeur d’une classe basée sur X.

Runtime

Lorsque vous lancez le programme, dès que la JVM tente d’interpréter l’instruction invokedynamic pour la première fois, la JVM va faire appel à la lambda metafactory, dont nous avons parlé précédemment. Dans notre exemple, lors du premier appel à makeX, la lambda metafactory génère une instance de X et lie dynamiquement la méthode run à la méthode lambda$0. Cette instance est alors conservée en mémoire. Au second appel à makeX, l’instance est restituée. Il s’agit par conséquent de la même instance qu’au premier appel.

Conclusion

L’utilisation d’une lambda expression dans notre exemple fait que la relation x1 == x2 est vérifiée. Tout du moins, il s’agit du comportement à terme de Java 8.

Ainsi, dans le cadre de Java 8, il peut être dangereux d’utiliser des mixins comme celui défini dans notre quizz de la semaine dernière ou comme celui que nous avons présenté dans notre article sur les méthodes virtuelles d’extension.

Références

Published by

Publié par François Sarradin

Consultant Java et λ développeur. Blog personnel : http://kerflyn.wordpress.com/ Twitter : @fsarradin

Commentaire

3 réponses pour " Solution du Quizz Java 8 "

  1. Published by , Il y a 11 ans

    Merci pour cette article, par contre je me demande le comportement de la Jvm en cas de (forte) concurrence. Y aura t il toujours qu’une seul instance? La lambda metafactory est elle propre a ta thread ou à ta jvm?

  2. Published by , Il y a 11 ans

    Très intéressant comme résultat !

  3. Published by , Il y a 11 ans

    @Duff: d’après le code de l’OpenJDK 8, ce que renvoit la lambda metafactory est un ConstantCallSite (http://docs.oracle.com/javase/7/docs/api/java/lang/invoke/ConstantCallSite.html). J’ignore pour le moment quelle garantie d’unicité offre ce call site. Mais au dire de Brian Goetz, l’unicité n’est en fait pas garantie. Le runtime fournit à sa « discrétion » ce qui est le plus efficace. Tout ce que vous devez savoir c’est que vous obtiendrez quelque chose qui implémente la bonne interface ;)

    En aparté : l’option -XDlambdaToMethod sera activée par défaut dès que le code de l’OpenJDK 8 sera transféré dans le JDK.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Nous recrutons

Être un Sapient, c'est faire partie d'un groupe de passionnés ; C'est l'opportunité de travailler et de partager avec des pairs parmi les plus talentueux.