- des améliorations au niveau des expressions switch, qui sont apparues pour la première fois dans Java 12 et Java 13 en tant que préversion et font désormais partie intégrante de Java 14 ;
- le pattern matching pour instanceof (une fonction de langage)
- Des NullPointerExceptions utiles (une fonctionnalité JVM)
Selon certains professionnels, l'instruction switch classique en Java n'est pas géniale. Contrairement à de nombreuses autres parties de Java, elle n'a pas été correctement repensée lors du retrait des fonctionnalités de C il y a toutes ces années. Le défaut clé est la « chute par défaut ». Cela signifie que si vous oubliez de mettre une clause break dans chaque cas, le traitement se poursuivra jusqu'à la clause case suivante.
Un autre défaut est que les variables sont étendues à l'intégralité du switch, vous ne pouvez donc pas réutiliser un nom de variable dans deux clauses case différentes. De plus, une clause par défaut n'est pas requise, ce qui laisse les lecteurs du code dans l'incertitude quant à savoir si une clause a été ou non oubliée.
Et bien sûr, il y a aussi la principale limitation : le type à activer ne peut être qu'un entier, une énumération ou une chaîne.
Dans le cadre du Project Amber, switch a bénéficié de quelques changements ! Ils ont été lancés en préversion dans JDK 12 et à nouveau dans 13 et sont destinés à être publiés dans JDK 14. Les développeurs vont disposer d'une nouvelle fonctionnalité lorsque switch sera utilisé comme instruction. En outre, il pourra également être utilisé comme une expression à l'avenir.
Les avantages des nouvelles expressions switch incluent une portée réduite pour les bogues en raison de l'absence de comportement de transition, l'exhaustivité et la facilité d'écriture grâce à l'expression et à la forme composée. À titre d'exemple de mise à jour, une expression switch peut désormais tirer parti de la syntaxe ->, comme dans cet exemple:
Code Java : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 | var log = switch (event) { case PLAY -> "User has triggered the play button"; case STOP, PAUSE -> "User needs a break"; default -> { String message = event.toString(); LocalDateTime now = LocalDateTime.now(); yield "Unknown event " + message + " logged on " + now; } }; |
Il faut noter également que switch peut désormais être utilisé comme expressions, c'est-à-dire qu'il peut retourner une valeur. Cela signifie que nous n'aurons pas à déclarer d'abord une variable, puis à affecter une valeur dans chaque branche. Combiné avec des étiquettes de flèche, il nous permet d'exprimer notre intention en beaucoup moins de lignes de code:
Code Java : | Sélectionner tout |
1 2 3 4 5 | String quantityString = switch (k) { case 1 -> "one"; case 2 -> "two"; default -> "many"; }; |
Parfois, vous pourriez être obligé d'exécuter un bloc de code dans le cadre d'une expression de cas. Afin de combiner cela avec la syntaxe d'étiquette de flèche, vous devez yield une valeur à la fin du bloc:
Code Java : | Sélectionner tout |
1 2 3 4 5 6 7 8 | DayType type = switch (day) { case 1, 2, 3, 4, 5 -> WEEKDAY; case 6, 7 -> WEEKEND; default -> { logger.warn(day + " is not a valid day. Legal values are [1..7]"); yield UNKNOWN; } }; |
Vous pouvez également transformer les instructions switch en expressions à l'aide des anciennes étiquettes de style en vous servant de yield.
Code Java : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 | Type type = switch (day) { case 1, 2, 3, 4, 5: yield WEEKDAY; case 6, 7: yield WEEKEND; default: logger.warn(day + " is not a valid day."); yield UNKNOWN; }; |
N'oubliez pas que c'est la syntaxe des libellés fléchés qui empêche les chutes. Cela signifie que si vous oubliez de yield, l'expression de cas suivante sera évaluée et vous pourriez vous retrouver avec le mauvais résultat.
Blocs de texte
Java 13 a introduit des blocs de texte comme fonction en préversion. Les blocs de texte facilitent le travail avec les littéraux de chaînes multilignes. Cette fonctionnalité est de nouveau en préversion sur Java 14 et intègre quelques ajustements. En guise de rappel, il est assez courant d'écrire du code avec de nombreuses concaténations de chaînes et séquences d'échappement afin de fournir une mise en forme de texte multiligne adéquate. Le code ci-dessous montre un exemple de mise en forme HTML:
Code Java : | Sélectionner tout |
1 2 3 4 5 | String html = "<HTML>" + "\n\t" + "<BODY>" + "\n\t\t" + "<H1>\"Java 14 is here!\"</H1>" + "\n\t" + "</BODY>" + "\n" + "</HTML>"; |
Avec les blocs de texte, vous pouvez simplifier ce processus et écrire du code plus élégant à l'aide des trois guillemets qui délimitent le début et la fin d'un bloc de texte:
Code Java : | Sélectionner tout |
1 2 3 4 5 6 | String html = """ <HTML> <BODY> <H1>"Java 14 is here!"</H1> </BODY> </HTML>"""; |
Les blocs de texte offrent également une plus grande expressivité par rapport aux littéraux de chaîne normaux.
Deux nouvelles séquences d'échappement ont été ajoutées dans Java 14. Premièrement, vous pouvez utiliser la nouvelle séquence d'échappement \s pour signifier un seul espace. Deuxièmement, vous pouvez utiliser une barre oblique inverse, \, comme moyen de supprimer l'insertion d'un nouveau caractère de ligne à la fin d'une ligne. Ceci est utile lorsque vous avez une très longue ligne que vous souhaitez diviser pour plus de lisibilité à l'intérieur d'un bloc de texte.
Par exemple, la façon actuelle de faire des chaînes multilignes est :
Code Java : | Sélectionner tout |
1 2 3 4 | String literal = "Lorem ipsum dolor sit amet, consectetur adipiscing " + "elit, sed do eiusmod tempor incididunt ut labore " + "et dolore magna aliqua."; |
Avec la séquence d'échappement \ dans des blocs de texte, cela peut s'exprimer comme suit:
Code Java : | Sélectionner tout |
1 2 3 4 5 | String text = """ Lorem ipsum dolor sit amet, consectetur adipiscing \ elit, sed do eiusmod tempor incididunt ut labore \ et dolore magna aliqua.\ """; |
Pattern Matching pour instanceof
En tant que développeur Java, vous avez probablement été dans une situation où vous devez vérifier si un objet est d'un certain type, et si c'est le cas le transtyper. Ce modèle est largement utilisé par exemple dans les implémentations equals. Java 14 introduit une fonctionnalité en préversion qui permet d'éliminer le besoin de transtypages explicites en passant par des vérifications instanceof. Par exemple, considérez le code suivant:
Code Java : | Sélectionner tout |
1 2 3 4 5 6 | if (obj instanceof Group) { Group group = (Group) obj; // use group specific methods var entries = group.getEntries(); } |
Cet extrait peut être réécrit en utilisant la fonction en préversion comme ceci :
Code Java : | Sélectionner tout |
1 2 3 | if (obj instanceof Group group) { var entries = group.getEntries(); } |
Étant donné que la vérification des conditions affirme que obj est de type Group, pourquoi devez-vous indiquer à nouveau que obj est de type Group avec le bloc de condition dans le premier extrait ? Ce besoin augmente potentiellement la possibilité d'erreurs.
La syntaxe plus courte supprimera de nombreux transtypages de programmes Java typiques.
« Il s'agit d'une fonctionnalité en préversion intéressante à expérimenter, car elle ouvre la porte à un pattern matching plus général. L'idée du pattern matching est de fournir une fonction de langage avec une syntaxe pratique pour extraire des composants d'objets en fonction de certaines conditions. C'est le cas avec l'opérateur instanceof, car la condition est une vérification de type et l'extraction appelle une méthode appropriée ou accède à un champ spécifique.
« En d'autres termes, cette fonctionnalité en préversion n'est que le début et vous pouvez vous attendre à une fonctionnalité de langage qui peut aider à réduire davantage la verbosité et ainsi réduire la probabilité de bogues ».
Les enregistrements
Il existe une autre fonctionnalité de langage en préversion : les enregistrements. Comme d'autres idées lancées jusqu'à présent, cette fonctionnalité suit la tendance de réduire la verbosité en Java et d'aider les développeurs à écrire du code plus concis. Les enregistrements se concentrent sur certaines classes de domaine dont le but est uniquement de stocker des données dans des champs et qui ne déclarent aucun comportement personnalisé.
Pour passer directement au problème, prenez une classe de domaine simple, BankTransaction, qui modélise une transaction avec trois champs: une date, un montant et une description. Vous devez vous soucier de plusieurs composants lorsque vous déclarez la classe :
- Le constructeur
- Les méthodes Getter
- toString()
- hashCode() et equals()
Le code de ces composants est souvent généré automatiquement par l'EDI et prend beaucoup de place. Voici une implémentation entièrement générée pour la classe BankTransaction:
Code Java : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | public class BankTransaction { private final LocalDate date; private final double amount; private final String description; public BankTransaction(final LocalDate date, final double amount, final String description) { this.date = date; this.amount = amount; this.description = description; } public LocalDate date() { return date; } public double amount() { return amount; } public String description() { return description; } @Override public String toString() { return "BankTransaction{" + "date=" + date + ", amount=" + amount + ", description='" + description + '\'' + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; BankTransaction that = (BankTransaction) o; return Double.compare(that.amount, amount) == 0 && date.equals(that.date) && description.equals(that.description); } @Override public int hashCode() { return Objects.hash(date, amount, description); } } |
Java 14 fournit un moyen de supprimer la verbosité et d'indiquer clairement que tout ce que vous voulez est une classe qui agrège uniquement les données avec les implémentations des méthodes equals, hashCode et toString. Vous pouvez réécrire BankTransaction comme suit:
Code Java : | Sélectionner tout |
1 2 3 | public record BankTransaction(Date date, double amount, String description) {} |
Avec un enregistrement, vous obtenez « automatiquement » les implémentations de equals, hashCode et toString en plus du constructeur et des Getters.
Les champs d'un enregistrement sont implicitement final. Cela signifie que vous ne pouvez pas les réaffecter. Notez, cependant, que cela ne signifie pas que l'ensemble de l'enregistrement est immuable; les objets eux-mêmes qui sont stockés dans les champs peuvent être mutables.
NullPointerExceptions
Considérez le code suivant :
Code Java : | Sélectionner tout |
var name = user.getLocation().getCity().getName();
Avant Java 14, vous pouvez obtenir l'erreur suivante:
Envoyé par Message d'erreur
Maintenant, avec Java 14, il existe une nouvelle fonctionnalité JVM à travers laquelle vous pouvez recevoir des diagnostics plus informatifs:
Envoyé par Message d'erreur
- La conséquence: Location.getCity() ne peut pas être invoquée.
- La raison: la valeur de retour de User.getLocation() est nulle.
Source : Oracle