Blog Arolla

Quoi de neuf dans Java 14 ?

Bientôt l’arrivée du pattern matching dans Java !

La 14ème version de Java est sortie le 17 mars. Notez que cette version de Java ne bénéficie pas du LTS (Long Time Support). La prochaine version de Java en bénéficiant sera la version 17 qui sortira en septembre 2021. Il ne s’agit cependant pas d’une petite mise à jour. En effet, le JDK 14 embarque plus d’une dizaine de nouvelles fonctionnalités, concernant à la fois le langage en lui-même mais aussi des améliorations de la JVM et d’autres outils. Découvrons ensemble les nouvelles possibilités qu’offre le JDK 14.

Nouveautés du langage

Pattern Matching avec instanceof

On le sait depuis un moment, Java souhaite implémenter le filtrage par motif (pattern matching) et a commencé depuis sa version 12 à enrichir ses switch. C’est désormais au tour du instanceof d’être amélioré. Lorsqu’on utilise l’opérateur instanceof, on est généralement obligé de caster l’objet sur lequel on vérifie l’instance afin de l’utiliser :
if (obj instanceof String) {
    String s = (String) obj;
    int length = s.length();
}
Maintenant, il n’est plus nécessaire de caster notre objet, on peut directement déclarer une variable après le instanceof, de cette manière :
if (obj instanceof String s) {
    int length = s.length();
}
Ou encore:
if (obj instanceof String s && !s.isBlank()) {.. s.contains(..) ..}

NullPointerException améliorée

On a tous déjà eu affaire à la fameuse NullPointerException de Java. Prenons par exemple ce message :
Exception in thread "main" java.lang.NullPointerExceptionat Prog.main(Prog.java:5)

Si à la ligne 5 de la classe Prog on trouve le morceau de code suivant :
a.i = 99;
Il est facile de déterminer l’origine de l’exception (a est null). Mais si par exemple on retrouve ce morceau de code là :
a.b.c.i = 99; // A ne pas reproduire, c'est pour l'exemple ;-)
Qu’est ce qui est null ? a, b, ou c ? Désormais, les messages des NullPointerException seront bien plus explicite. On trouvera ainsi par exemple :
Exception in thread "main" java.lang.NullPointerException: Cannot read field "c" because "a.b" is nullat Prog.main(Prog.java:5)
Voici la liste des différents nouveaux messages :
  • Cannot load from <element type> array
  • Cannot read the array length
  • Cannot store to <element type> array
  • Cannot throw exception
  • Cannot read field "<field name>"
  • Cannot invoke "<method>"
  • Cannot enter synchronized block
  • Cannot exit synchronized block
  • Cannot assign field "<field name>"
Notez toutefois que les messages détaillés des NullPointerException est désactivée par défaut dans le JDK 14. Pour l'activer vous devrez utiliser l'option :
-XX:+ShowCodeDetailsInExceptionMessages
Bien plus simple pour débuguer n’est-ce pas ?

Les Records

Un record est un nouveau genre de déclaration de type en Java. Tout comme un enum, il s’agit d’une class avec des restrictions. La déclaration d’un record se fait en lui donnant un nom et un état (ses attributs) :
record Point(int x, int y) { }
L’équivalent du record ci-dessus en class serait :
    public final class Point{

        private final int x;
        private final int y;

        public Point(int x, int y) {
            this.x = x;
            this.y = y;
        }

        public int getX() {
            return x;
        }

        public int getY() {
            return y;
        }

        @Override
        public boolean equals(Object o) {
            // Implémentation d'equals en utilisant x et y
        }

        @Override
        public int hashCode() {
            // Implémentation d'hashCode en utilisant x et y
        }

        @Override
        public String toString() {
            return "Point{" +
                    "x=" + x +
                    ", y=" + y +
                    '}';
        }
    }
Implicitement, un record contient :
  • Des champs private final
  • Un constructeur public
  • Des getters
  • Une implémentation des méthodes equals, hashCode et toString qui utilise tous les attributs de celui-ci
Les records ne peuvent pas hériter d’autres classes, ni contenir des champs supplémentaires que ceux décrivant l’état du record (exceptés des variables static). Ils sont implicitement final et ne peuvent pas être abstract. Derrière toutes ces contraintes, l’objectif est d’appliquer la politique de l’immutable par défaut, qui est largement préférable aux objets contenant des données. Il est cependant possible d’explicitement déclarer des membres générés automatiquement, comme par exemple le constructeur :
record Range(int lo, int hi) {
  public Range {
    if (lo > hi)
      throw new IllegalArgumentException(String.format("(%d,%d)", lo, hi));
  }
}
Les records permettent d’éviter les erreurs ou oublis lorsqu’on rajoute un attribut à un POJO, comme la prise en compte de ce nouvel attribut dans equals, hashCode et toString et ainsi devenir source de bug. Enfin, et ce n’est pas le moins important, les records vont faire réfléchir à deux fois un développeur qui souhaite modifier le model en rajoutant simplement un nouvel attribut à un objet porteur de données alors qu’il serait peut-être plus judicieux de créer une nouvelle classe.

Amélioration du switch

Initialement sorti en preview avec Java 12, puis légèrement modifié avec Java 13, c’est finalement avec le JDK 14 que les switch améliorés sont disponibles en version finale. Pour rappel, le switch peut désormais s’écrire de cette manière, sans break :
switch (day) {
    case MONDAY, FRIDAY, SUNDAY -> System.out.println(6);
    case TUESDAY                -> System.out.println(7);
    case THURSDAY, SATURDAY     -> System.out.println(8);
    case WEDNESDAY              -> System.out.println(9);
}
Mieux encore, le switch devient une expression à part entière :
int j = switch (day) {
    case MONDAY  -> 0;
    case TUESDAY -> 1;
    default      -> {
        int k = day.toString().length();
        int result = f(k);
        yield result;
    }
};
Notez l’utilisation du mot clef yield pour retourner la valeur dans le cas d’un bloc de code.

Text Blocks

Initialement sorti avec le JDK 13, les Text Blocks permettent l’instanciation de chaines de caractères sur plusieurs lignes sans concaténation :
String query = """
               SELECT `EMP_ID`, `LAST_NAME` FROM `EMPLOYEE_TB`
               WHERE `CITY` = 'INDIANAPOLIS'
               ORDER BY `EMP_ID`, `LAST_NAME`;
               """;
Avec Java 14, les Text Blocks sont toujours en preview mais avec quelques nouveautés permettant le contrôle des sauts de ligne et des espaces :
String text = """
                Lorem ipsum dolor sit amet, consectetur adipiscing \
                elit, sed do eiusmod tempor incididunt ut labore \
                et dolore magna aliqua.\
                """;

// Equivaut à
String literal = "Lorem ipsum dolor sit amet, consectetur adipiscing " +
                 "elit, sed do eiusmod tempor incididunt ut labore " +
                 "et dolore magna aliqua.";
Et pour spécifier des espaces, on peut utiliser le caractère unicode \s  :
String colors = """
    red  \s
    green\s
    blue \s
    """;

Modifications apportées aux Garbage Collectors

Implémentation du NUMA pour G1

Le ramasse miette G1 (Garbage First), qui est devenu le garbage collector par défaut depuis Java 9, s’est vu implémenter un nouveau système d’allocation mémoire compatible avec les architectures NUMA (pour Non Uniform Memory Architecture, système dans lequel les zones mémoires sont séparées), ce qui devrait largement améliorer les performances des applications Java sur les machines avec plusieurs processeurs.
Schéma de principe d'une architecture NUMA

ZGC sur Windows et macOS

Z Garbage Collector est un nouveau ramasse miettes apparu avec Java 11, toujours expérimental, et désormais compatible avec Windows et macOS. Il se veut scalable et à faible latence (moins de 10ms), pouvant gérer des tas de quelques centaines de mégaoctets à plusieurs téraoctets avec le même temps de pause.

Outils

jpackage, un nouvel outil de packaging

Le besoin derrière cet outil nommé jpackage (qui est en toujours en phase d’incubation dans cette version du JDK) est de permettre la création d’un exécutable propre au système d’exploitation au lieu d’un simple JAR. Ainsi pour Windows on peut désormais packager notre application directement en exe ou msi, pkg ou dmg sur macOS et deb ou rpm sur Linux.

Streaming des événements du JDK Flight Recorder

Java Flight Recorder (JFR) est un outil de collecte de données et de diagnostic d’application Java. Avec comme objectif d’utiliser JFR pour faire du monitoring d’application en plus que du profilage, il est désormais possible de consulter les données émises par le Java Flight Recorder de manière asynchrone :
try (var rs = new RecordingStream()) {
  rs.enable("jdk.CPULoad").withPeriod(Duration.ofSeconds(1));
  rs.enable("jdk.JavaMonitorEnter").withThreshold(Duration.ofMillis(10));
  rs.onEvent("jdk.CPULoad", event -> {
    System.out.println(event.getFloat("machineTotal"));
  });
  rs.onEvent("jdk.JavaMonitorEnter", event -> {
    System.out.println(event.getClass("monitorClass"));
  });
  rs.start();
}

Dépréciations et suppressions

Il n’y a pas que de nouvelles features dans le JDK 14, il est aussi temps d’un petit ménage de printemps.

Portage vers Solaris et SPARC

Dans le but d’accélérer le développement de nouvelles fonctionnalités, le portage du JDK 14 vers le système d’exploitation Solaris et les architectures de processeur SPARC est déprécié et sera prochainement supprimé. Les développeurs qui souhaitent que ces portages soient toujours maintenus sont invités à exprimer leur souhait auprès d’Oracle.

Suppression du ramasse miette Concurrent Mark Sweep (CMS)

Toujours avec comme objectif d’améliorer ou de développer de nouveaux ramasses miettes pour Java, et notamment G1, le garbage collector Concurrent Mark Sweep (CMS), qui est déprécié depuis Java 9 est désormais supprimé.

Utilisation combinée des ramasses miettes Parallel Scavenge et Serial Old

Dans de très rares cas, les ramasses miettes nommés « Parallel Scavenge » et « Serial Old » sont utilisés de manière simultanée sans grande plus-value, comparée au cout de maintenance de cette combinaison de ramasses miettes, qui est maintenant dépréciée.

Pack200

Pack200 est un outil permettant de transformer des fichiers JAR en fichier compressé pack200 permettant de gagner de la bande passante lors de leur téléchargement. Cet outil est déprécié depuis Java 11 et est maintenant supprimé avec Java 14.

Migration vers Java 14

Si vous développez avec Java 11 ou plus : à moins d’utiliser Pack200 ou Concurrent Mark Sweep (qui sont dépréciés depuis Java 11 et supprimés avec le JDK 14), la migration se fera sans aucun problème. Si vous utilisez une version précédente à Java 11, il est plutôt recommandable d’utiliser la version LTS (Java 11 donc), avant d’envisager une migration vers le JDK 14. Pour développer avec le JDK 14, il est recommandé d’utiliser IntelliJ IDEA 2020.1 (il faudra choisir 14 (Preview) comme niveau de langage dans les options du projet pour profiter des Records ou du pattern matching avec instanceof) ou Eclipse IDE 2020-03.

Conclusion

Le développement de Java continue son évolution et son amélioration, en mettant l’accent sur la correction des points faibles du langage, comme par exemple le fait que Java soit trop verbeux. Le pattern matching devrait arriver très prochainement dans Java, en tout cas avant Java 17 (la prochaine version bénéficiant du support à long terme) prévu pour septembre 2021. Java (re)devient enfin un sérieux concurrent à NodeJS, ou Python, mais aussi et surtout aux autres langages de la JVM, comme Kotlin et Scala. Serait ce le début de la fin pour ces langages ? Dans tous les cas, rendez-vous en septembre pour découvrir ce que Java 15 nous réservera.
Plus de publications

Laisser un commentaire

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