Java 14 : Text Blocks
Ce quatrième article détaille les blocs de texte (Text Blocks) proposés en mode preview en Java 14.
Cet article fait partie d’une série détaillant les principales fonctionnalités proposées dans Java 14 :
- Java 14 : Vue d’ensemble
- Java 14 : Les records
- Java 14 : Switch expression
- Java 14 : Text Blocks
- Java 14 : Pattern matching avec l’instruction instanceof
Historiquement, le support des chaînes de caractères littérales multi-lignes souffre de plusieurs problèmes essentiellement dus au fait qu’une chaîne littérale est mono-ligne. Cela oblige à concaténer les différentes lignes en ajoutant à la fin de chacune d’elle une séquence d’échappement représentant un retour chariot. Il est aussi nécessaire d’échapper les doubles quotes.
String html = "<HTML>\n" +
" <BODY>\n" +
" <H1>"Oxiane"</H1>\n" +
" </BODY>\n" +
"</HTML>\n";
Cela rend la saisie, la lecture et la maintenance de ces chaînes complexes et favorise les bugs.
Les IDE facilitent la saisie de telles chaînes littérales : par exemple Eclipse propose simplement de faire un copier/coller de la chaîne multi-lignes brute après une double quote. Il se charge alors de rajouter les éléments nécessaires : double quotes en début et fin de ligne, retours chariot, opérateur +, échappement des caractères requis.
Cela ne facilite cependant pas la lecture et la maintenance.
De nombreux langages proposent différentes solutions pour un support des chaînes de caractères multi-lignes. La solution proposée en preview dans la syntaxe du langage Java est d’utiliser des blocs de texte (Text Blocks).
Initialement le support des chaînes de caractères multi-lignes était prévu sous la forme des raw string en Java 12. Finalement c’est sous une forme différente dans la JEP 354, les Text Blocks, que ce support est proposé en preview en Java 13.
En Java 14, ils ont été légèrement modifiés et proposés dans une seconde version preview. Ces modifications concernent l’ajout de deux nouvelles séquences d’échappement.
Les blocs de texte facilitent l’insertion de chaînes de caractères littérales formatées comme du SQL, JSON, XML, HTML, du code Java, …
String html = """
<HTML>
<BODY>
<H1>"Oxiane"</H1>
</BODY>
</HTML>""";
La syntaxe
Le contenu d’un bloc de texte est entouré par un délimiteur de début :
- 3 double quotes qui se suivent : """
- Zéro ou plusieurs caractères espaces
- Un retour chariot
Et un délimiteur de fin :
- 3 double quotes qui se suivent : """
jshell> var texte = """
...> Oxiane"""
texte ==> "Oxiane"
| created variable texte : String
Un bloc de texte ne peut donc pas être sur une seule ligne : ainsi un bloc texte vide nécessite deux lignes de code.
jshell> var texte = """"""
| Error:
| illegal text block open delimiter sequence, missing line terminator
| var texte = """"""
| ^
jshell> var texte = """
...> """
texte ==> ""
| modified variable texte : String
| update overwrote variable texte : String
jshell>
Un bloc de texte peut contenir zéro ou plusieurs caractères notamment des retours chariot, des tabulations et même des doubles quotes sans échappement.
jshell> var texte = """
...> Bienvenue "Oxiane",
...> Exemple de bloc de texte
...> avec un contenu multi-lignes"""
texte ==> "Bienvenue "Oxiane",\nExemple de bloc de texte\navec un contenu multi-lignes"
| modified variable texte : String
| update overwrote variable texte : String
La gestion de l’indentation accessoire/significative
Dans un bloc de texte, il y a une gestion de l’indentation qui est distinguée en deux formes :
- L’indentation accessoire qui est déterminée et supprimée sur toutes les lignes
- L’indentation significative qui est conservée
L’indentation accessoire est définie en déplaçant le contenu vers la droite. Les espaces et les tabulations sont pris en compte pour déterminer l’indentation.
jshell> String html = """
...> <HTML>
...> <BODY>
...> <H1>"Oxiane"</H1>
...> </BODY>
...> </HTML>""";
html ==> "<HTML>\n <BODY>\n <H1>"Oxiane"</H1>\n </BODY>\n</HTML>"
| created variable html : String
jshell> println(html)
<HTML>
<BODY>
<H1>"Oxiane"</H1>
</BODY>
</HTML>
jshell>
Pour définir une indentation, il est possible de déplacer le délimiteur de fermeture vers la gauche sur sa propre ligne.
jshell> String html = """
...> <HTML>
...> <BODY>
...> <H1>"Oxiane"</H1>
...> </BODY>
...> </HTML>
...> """;
html ==> " <HTML>\n <BODY>\n <H1>"Ox ... </BODY>\n </HTML>\n"
| modified variable html : String
| update overwrote variable html : String
jshell> println(html)
<HTML>
<BODY>
<H1>"Oxiane"</H1>
</BODY>
</HTML>
jshell>
Attention cela ajoute une ligne blanche à la fin. Il est possible d’utiliser la séquence d’échappement \<retour_chariot> pour demander la concaténation avec la ligne suivante qui est vide puisqu’elle ne contient qu’une indentation accessoire.
jshell> String html = """
...> <HTML>
...> <BODY>
...> <H1>"Oxiane"</H1>
...> </BODY>
...> </HTML>\
...> """;
html ==> " <HTML>\n <BODY>\n <H1>"Ox ... </BODY>\n </HTML>"
| modified variable html : String
| update overwrote variable html : String
jshell> println(html)
<HTML>
<BODY>
<H1>"Oxiane"</H1>
</BODY>
</HTML>
jshell>
Il est aussi possible d’utiliser la méthode indent()
de la classe String
pour ajouter une indentation significative.
jshell> String html = """
...> <HTML>
...> <BODY>
...> <H1>"Oxiane"</H1>
...> </BODY>
...> </HTML>""".indent(4);
html ==> " <HTML>\n <BODY>\n <H1>"Oxiane" ... </BODY>\n </HTML>\n"
| modified variable html : String
| update overwrote variable html : String
jshell> println(html)
<HTML>
<BODY>
<H1>"Oxiane"</H1>
</BODY>
</HTML>
jshell>
L’IDE IntelliJ IDEA affiche une démarcation entre l’indentation accessoire et significative dans les blocs de texte :
Les séquences d’échappement
Un bloc de texte peut contenir des caractères d’échappement mais c’est rarement utile sauf pour quelques cas particuliers.
La JEP 368 de Java 14 a introduit deux nouvelles séquences d’échappement :
- \s : pour indiquer un espace. Cela peut, par exemple, être utile pour conserver des espaces en fin de ligne qui seraient sans cela retirés par le compilateur
jshell> String html = """
...> <HTML> \s
...> <BODY> \s
...> <H1>"Oxiane"</H1> \s
...> </BODY> \s
...> </HTML>"""
html ==> "<HTML> \n <BODY> \n <H1>"Oxiane"</H1> \n </BODY> \n</HTML>"
| modified variable html : String
| update overwrote variable html : String
jshell>
- \<retour_chariot> : pour supprimer la fin de ligne. Cela revient à concaténer la ligne courante avec la suivante
jshell> String html = """
...> <HTML>\
...> <BODY>\
...> <H1>"Oxiane"</H1>\
...> </BODY>\
...> </HTML>"""
html ==> "<HTML> <BODY> <H1>"Oxiane"</H1> </BODY></HTML>"
| modified variable html : String
| update overwrote variable html : String
jshell> println(html)
<HTML> <BODY> <H1>"Oxiane"</H1> </BODY></HTML>
jshell>
Les triples double quotes dans le texte doivent être échappées : le plus simple est d’échapper uniquement la première double quote.
jshell> """
...> """Oxiane""" """
| Error:
| ';' expected
| """Oxiane""" """
| ^
| Error:
| illegal text block open delimiter sequence, missing line terminator
| """Oxiane""" """
| ^
| Error:
| illegal text block open delimiter sequence, missing line terminator
| """Oxiane""" """
| ^
jshell> """
...> """Oxiane""" """
$22 ==> """"Oxiane""""
| created scratch variable $22 : String
jshell>
Le traitement par le compilateur
Le traitement des blocs de texte par le compilateur se fait en 3 étapes :
- Les retours chariot sont normalisés en LF (\u000A) pour garantir le support multi-plateforme
- La détermination et la suppression de l’indentation accessoire en début de chaque ligne et des espaces à la fin de chaque ligne
- L’interprétation des séquences d’échappement dans le contenu
A l’issu du traitement par le compilateur, les blocs de texte sont compilés vers le type java.lang.String
jshell> String html = """
...> <html>\r
...> <body>\r
...> <p>Oxiane</p>\r
...> </body>\r
...> </html>\r
...> """;
html ==> "<html>\r\n <body>\r\n <p>Oxiane</p>\r\n </body>\r\n</html>\r\n"
| modified variable html : String
| update overwrote variable html : String
jshell>
L’utilisation des blocs de texte
Un bloc de texte est utilisable partout où une chaîne de caractères littérale peut être attendue puisque suite à leur compilation, ils sont transformés en objets de type String
dans le bytecode.
jshell> """
...> Oxiane""".toUpperCase();
$18 ==> "OXIANE"
| created scratch variable $18 : String
jshell> """
...> Oxiane""".equals("Oxiane")
$19 ==> true
| created scratch variable $19 : boolean
jshell> System.out.println("""
...> Oxiane""")
Oxiane
jshell>
Conclusion
Les blocs de texte proposent enfin une solution simple qui permet de saisir des chaînes de caractères multilignes.
Ils offrent aussi une souplesse pour gérer une indentation accessoire. Ceci est particulièrement pratique pour inclure du code formatté dans des chaînes de caractères.
Avec la JEP 378, les blocs de texte devraient devenir standard en Java 15 sans modification complémentaire.
Le prochain article de cette série détaillera une autre évolution dans la syntaxe du langage Java : le pattern matching dans l’instruction instanceof.