Quantcast
Channel: Publicis Sapient Engineering – Engineering Done Right
Viewing all articles
Browse latest Browse all 1865

Solution du Quizz Java 8

$
0
0

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


Viewing all articles
Browse latest Browse all 1865

Trending Articles