JDK10, 11 et 12, quelques nouveautés
Depuis JDK9, Oracle change le cycle de sorties des différentes versions de Java. Alors qu'avant, une nouvelle version de Java sortait tous les 2, 3 ans, c'est maintenant tous les 6 mois qu'une nouvelle version sera disponible.
Parmi ces version, certaines sont étiquetées long term support (LTS) et sont supportées par Oracle plusieurs années tandis que, pour les autres, le support ne durera que 6 mois. Les « prochaines » versions LTS de Java sont la 8 et la 11.
Ce billet recense quelques nouveautés des versions 10, 11 et 12 de Java et ne sera pas exhaustif. Je ne m'intéresse qu'à quelques fonctionnalités visibles pour les développeurs et les développeuses. Mes sources principales sont les pages Oracle recensant les JDK Enhanced Proposal (JEP).
JDK10
Cette version du JDK voit passer plusieurs améliorations du garbage collecor (GC), par exemple du parallélisme JEP307, une amélioration du class data sharing (CDS) introduit avec JDK5 JEP301,la possibilité d'allouer de la mémoire sur d'autres périphériques JEP316, des améliorations internes…
Ce qui est le plus visible dans cette version 10, est l'inférence de type.
Inférence du type des variables locales
Local-variable type inference JEP286 étend l'inférence de type aux déclarations de variables locales avec initialiseurs, la variable d'un enhanced for et les variables d'un for. Le mot utilisé est var. Il n'est pas un mot-clé (keyword) mais un nom de type réservé ce qui limitera l'impact de l'ajout sur les codes existants.
Il est donc possible d'écrire :
var i = 5;
var s = "Hello world";
var l = new ArrayList<String>();
var heu = videos.stream()
.map(v -> v.getAuthor())
.sorted()
.distinct()
.count();
var j = foo(i);
Le troisième exemple me convainc peu par-ce que je préfère que l
soit de type
List<String>
et pas ArrayList<String>
mais j'aime bien le quatrième exemple
où l'on peut envisager que lorsque l'on commence à enchainer les appels de
méthodes, on ne sache pas encore très bien quel sera le type « final ».
Il est à noter que cela ne fonctionnera pas pour un tableau ou null
.
Certaines personnes ajoutent que var
amène une meilleur lisibilité de l'indentation (puisque le « type » fait toujours 3 lettres).
Pour plus de détails, lire par exemple l'article de Nicolai Parlog chez codefx. Lire également les règles de bonnes pratiques.
JDK11
La version 11 apporte — à mon sens — deux changements visibles pour les développeurs lambdas.
Local-variable syntax for lambda parameters
local-variable syntax for lambda parameters ajoute le mot clé var
aux
lambdas. Là où l'on écrivait :
(x, y) -> x.process(y)
on pourra écrire :
(var x, var y) -> x.process(y)
Cet ajout est principalement là dans un soucis d'uniformisation de la syntaxe JEP323
Launch single-file source-code programs
Ça y est, Java n'est plus compilé et on peut écrire des scripts Java ! Carrément des shebang files JEP330. En fait, ce n'est pas vrai mais c'est proche.
Pour rappel, le lanceur Java peut lancer un fichier class
, lancer la classe principale d'un
fichier jar
ou encore lancer la classe principale d'un module. Aujourd'hui,
il peut également lancer une classe déclarée dans un fichier source.
On peut donc écrire :
public class Hello {
public static void main (String[] args) {
System.out.println("Hello " + args[0]);
}
}
et exécuter le programme avec la commande suivante. Aucun fichier .class
n'est
généré, le bytecode se trouve en mémoire.
$ java Hello.java Alice
Si mon fichier ne respecte pas la convention de nommage java et s'appelle par
exemple hello
, je peux écrire :
$ java --source 11 hello Alice
Il reste un pas à franchir pour écrire un script en Java. Allons-y. J'écris le code suivant dans un fichier nommé hello
et je rends le fichier exécutable.
#!/usr/lib/jvm/java-11-openjdk-amd64/bin/java --source 11
public class Hello {
public static void main (String[] args) {
System.out.println("Hello " + args[0]);
}
}
Je peux alors entrer la commande :
./hello Alice
Dans les changements peu visibles pour les développeurs et développeuses, je note :
nest-based access control JEP181 concernant les classes internes.
Pour une classe
Outer
et une classe interneInner
, les bytecodes générés dansOuter.class
etOuter$Inner.class
seront différents par rapport aux bytecodes générés dans les versions antérieures de Java et le contrôle d'accès au code amélioré.Pour comprendre : la JEP181, un article de Ganesh Pagade chez Baeldung et de Peter Verhas chez dzone.
dynamic class-file constants JEP309 est complexe. Le but est d'étendre le constant pool et d'y ajouter d'autres types de valeurs.
Actuellement, Java utilise une table contenant les constantes du programme, le (constant pool). C'est assez facile à comprendre pour les types primitifs et les chaines (
String
). C'est d'ailleurs pour ça que :String s1 = "Marlène"; String s2 = "Marlène"; System.out.println(s1 == s2); // true
Au fil des versions de Java, d'autres valeurs ont été ajoutées à ce constant pool comme les « littéraux de classes constants » (class literal constants). Toutes les classes, les interfaces, les énumérations… sont des instances de
Class
dans une application Java etClass
permet par exemple l'introspection :Class
c = String.class; System.out.println(Arrays.toString(c.getMethods())); String.class
est une instance deClass<String>
comme"Hello world"
est une instance deString
.String.class
est constant et se trouve dans le constant pool.Actuellement si
A
etB
sont deux constantes et se trouvrent donc dans le constant pool,Math.max(A, B)
ne s'y trouve pas. Ceci devrait évoluer.HTTP client standardisé comme présenté dans le JDK9. J'en avais parlé dans ce billet.
mise à jour pour rester up to date : Key Agreement with Curve25519 and Curve448, ChaCha20 and Poly1305 cryptographic algorithms, Unicode 10 et TLS1.3
Pour le reste dans les changements apportés au JDK et non présentés ici, ça se passe là JDK11
JDK12
Dans cette version du JDK, une seule fonctionnalité est destinée aux développeurs et développeuses « standard ». Il s'agit du switch qui devient une expression (JEP325). switch pourra être utilisé comme une instruction — comme avant — ou comme une expression.
Cette fonctionnalité est en preview
c'est-à-dire qu'elle est pleinement fonctionnelle et documentée et est présente
afin d'être complètement testée. Elle pourrait disparaitre et pour être utilisée
il est nécessaire de compiler et d'exécuter son code avec le switch --enable-preview
.
Au
case <label>:
s'ajoute uncase <label> ->
Ceci permet de ne pas utiliser de
break
car seule l'instruction (ou le bloc d'instructions) suivant la flèche sera exécuté. On pourra donc écrire, pour peu qu'uneenum
Season existe :Season s = // a season switch (s) { case SPRING, SUMMER -> System.out.println("It's hot"); case WINTER, AUTUMN -> System.out.println("It's cold"); default -> System.out.println("Claim climate change"); }
Le switch est désormais également une expression, il a un type et une valeur. On pourra par exemple écrire :
Season s = // a season String message = switch(s){ case SPRING, SUMMER -> "It's hot"; case WINTER, AUTUMN -> "It's cold"; };
Les autres fonctionnalités concernent la java virtual machine (JVM). Le garbage collecor (GC) : ajout de Shenandoah, un GC à faible temps de pause à titre expérimental et, à ma connaissance, c'est toujours G1 qui est utilisé. Optimisation de G1 pour qu'il rende plus rapidement la mémoire non utilisée du tas (heap) au système lorsque le processus Java est en pause. Pratique pour les environnements conteneurisés et possibilité de fixer un objectif de temps maximal d'exécution du garbage collector. Au sujet des architectures, One AArch64 Port, Not Two, supprime un des deux ports de Java pour les architectures ARM pour n'en conserver qu'une seule.
Pour les détails, ça se passe là JDK12
Au vu des cycles de développement, je pense que je vais avoir tendance à utiliser les versions LTS.
Crédit photo personnel. Petit souvenir de quelques jours à la mer…