Points Clés
- Java SE 13 (septembre 2019) a introduit les blocs de texte en tant que fonctionnalité en preview, visant à réduire la peine pour déclarer et utiliser des chaînes de caractères littérales multilignes en Java. Il a ensuite été affiné dans une deuxième preview, avec des modifications mineures, et devrait devenir une fonctionnalité permanente du langage Java dans Java SE 15 (septembre 2020).
- Les chaînes littérales dans les programmes Java ne sont pas limités à des chaînes courtes comme "oui" et "non"; ils correspondent souvent à des «programmes» entiers dans des langages structurés tels que HTML, SQL, XML, JSON ou même Java.
- Les blocs de texte sont des chaînes littérales qui peuvent comprendre plusieurs lignes de texte et utilisent des triples double quotes (""") comme délimiteur d'ouverture et de fermeture.
- Un bloc de texte peut être considéré comme un bloc de texte bidimensionnel incorporé dans un programme Java.
- Le fait de pouvoir conserver la structure bidimensionnelle de ce programme intégré, sans avoir à l'encombrer de caractères d'échappement et d'autres intrusions linguistiques, est moins sujet aux erreurs et se traduit par des programmes plus lisibles.
Durant Java Futures à QCon New York, l'architecte du langage Java Brian Goetz nous a fait faire un tour éclair de certaines des fonctionnalités récentes et futures du langage Java. Dans cet article, il plonge dans les blocs de texte (Text Blocks).
Java SE 13 (septembre 2019) a présenté les blocs de texte en tant que fonctionnalité en preview, visant à réduire la peine pour déclarer et d'utiliser des chaînes littérales multi-lignes en Java.
Elle a ensuite été affinée dans une deuxième preview, avec des modifications mineures, et devrait devenir une fonctionnalité permanente du langage Java dans Java SE 15 (septembre 2020).
Les blocs de texte sont des chaînes littérales qui peuvent contenir plusieurs lignes de texte. Un bloc de texte ressemble à :
String address = """
25 Main Street
Anytown, USA, 12345
""";
Dans cet exemple simple, la variable adress
contiendra une chaîne de deux lignes, avec des terminateurs de ligne après chaque ligne. Sans blocs de texte, nous aurions dû écrire :
String address = "25 Main Street\n" +
"Anytown, USA, 12345\n";
or
String address = "25 Main Street\nAnytown, USA, 12345\n";
Comme tous les développeurs Java le savent déjà, ces alternatives sont lourdes à écrire. Mais, plus important encore, ils sont également plus sujets aux erreurs (il est facile d'oublier un \n
et de ne pas s'en rendre compte), et plus difficiles à lire (car la syntaxe du langage est mélangée au contenu de la chaîne). Puisqu'un bloc de texte est généralement exempt de caractères d'échappement et d'autres interruptions linguistiques, il laisse le langage s'écarter afin qu'il soit plus facile pour les lecteurs de voir le contenu de la chaîne.
Le caractère échappé le plus courant dans les chaînes littérales est la nouvelle ligne (\n
), et les blocs de texte éliminent leur besoin en permettant l'expression directe de chaînes multi-lignes. Après la nouvelle ligne, le caractère échappé suivant le plus souvent est la double quote (\"
), qui doit être échappé car il entre en conflit avec le délimiteur littéral de la chaîne. Les blocs de texte éliminent également la nécessité de ceux-ci, car un seul caractère double quote n'entre pas en conflit avec le délimiteur triple double quotes des blocs de texte.
Pourquoi ce drôle de nom ?
On pourrait penser que cette fonctionnalité aurait été appelée "chaînes littérales multi-lignes" (et, probablement, c'est comme cela que beaucoup de gens l'appelleront.) Mais, nous avons choisi un nom différent, blocs de texte, pour mettre en évidence le fait qu'un bloc de texte n'est pas simplement une collection de lignes sans rapport, mais qu'il vaut mieux le considérer comme un bloc de texte bidimensionnel incorporé dans un programme Java. Pour illustrer ce que nous entendons par "à deux dimensions", prenons un exemple légèrement plus structuré, où notre bloc de texte est un extrait d'un document XML. (Les mêmes considérations s'appliquent aux chaînes qui sont des extraits de "programmes" dans un autre "langage", tels que SQL, HTML, JSON ou même Java, qui sont incorporés en tant que littéraux dans un programme Java.)
void m() {
System.out.println("""
<person>
<firstName>Bob</firstName>
<lastName>Jones</lastName>
</person>
""");
}
Qu'est-ce que l'auteur attend de cela à l'affichage ? Bien que nous ne puissions pas lire dans leurs pensées, il semble peu probable que l'intention était que le bloc XML soit indenté de 21 espaces; il est beaucoup plus probable que ces 21 espaces soient là uniquement pour aligner le bloc de texte avec le code environnant. D'un autre côté, l'auteur a presque certainement l'intention que la deuxième ligne affichée soit indentée de quatre espaces de plus que la première. De plus, même si l'auteur voulait exactement 21 espaces d'indentation, que se passe-t-il lorsque le programme est modifié et que l'indentation du code environnant change ? Nous ne voudrions pas que l'indentation de la sortie change simplement parce que le code source a été reformaté - nous ne voudrions pas non plus que le bloc de texte semble "hors de propos" par rapport au code environnant car il ne s'aligne pas dans un manière sensée.
À partir de cet exemple, nous pouvons voir que l'indentation naturelle d'un bloc de texte multiligne incorporé dans notre source de programme dérive à la fois de l'indentation relative souhaitée entre les lignes du bloc, et de l'indentation relative entre le bloc et le code environnant. Nous voulons que nos chaînes littérales s'alignent avec notre code (car ils sembleraient déplacés s'ils ne le faisaient pas), et nous voulons que les lignes de nos chaînes littérales reflètent l'indentation relative entre les lignes, mais ces deux sources d'indentation - que nous pouvons appeler accessoire et essentiel - sont nécessairement mélangés dans la représentation source du programme. (Les chaînes littérales traditionnelles n'ont pas ce problème car ils ne peuvent pas s'étendre sur les lignes, donc il n'y a pas de tentation de mettre des espaces de tête supplémentaires à l'intérieur du littéral juste pour aligner les choses.)
Une façon de résoudre ce problème consiste à utiliser une méthode de bibliothèque que nous pouvons appliquer aux chaînes littérales multilignes, tels que trimIndent, et Java fournit effectivement une telle méthode : String::stripIndent. Mais comme il s'agit d'un problème si courant, Java va plus loin, supprimant automatiquement l'indentation accessoire au moment de la compilation.
Pour démêler l'indentation accessoire et essentielle, nous pouvons imaginer dessiner le plus petit rectangle possible autour de l'extrait XML qui contient l'extrait entier, et traiter le contenu de ce rectangle comme un bloc de texte bidimensionnel. Ce «rectangle magique» est le contenu du bloc de texte et reflète l'indentation relative entre les lignes du bloc, mais ignore toute indentation qui est un artefact de la façon dont le programme est indenté.
Cette analogie du "rectangle magique" peut aider à motiver le fonctionnement des blocs de texte, mais les détails sont un peu plus subtils car nous souhaitons peut-être un contrôle plus fin de l'indentation considérée comme accessoire par rapport à essentielle. L'équilibre entre l'indentation accessoire et l'indentation essentielle peut être ajusté en utilisant la position du délimiteur arrière par rapport au contenu.
Les détails
Un bloc de texte utilise des triples doubles quotes ("""
) comme délimiteur d'ouverture et de fermeture, et le reste de la ligne avec le délimiteur d'ouverture doit être vide. Le contenu du bloc de texte commence à la ligne suivante et se poursuit jusqu'au délimiteur de fermeture. Le traitement à la compilation du contenu du bloc se déroule en trois phases :
- Les terminateurs de ligne sont normalisés. Tous les terminateurs de ligne sont remplacés par le caractère LF (
\u000A
). Cela empêche la valeur d'un bloc de texte d'être affectée silencieusement par les conventions de nouvelle ligne de la plate-forme sur laquelle le code a été modifié pour la dernière fois. (Windows utiliseCR
+LF
pour terminer les lignes; les systèmes Unix utilisent uniquementLF
, et il existe même d'autres schémas utilisés également.) - Les espaces de tête accessoires et tout les espaces à l'extrémitéce sont supprimés de chaque ligne. Les espaces accessoires sont déterminés comme suit :
- Calculez un ensemble de lignes déterminantes, qui sont toutes les lignes non vierges du résultat de l'étape précédente, ainsi que la dernière ligne (la ligne qui contient le délimiteur de fermeture) même si il est vide;
- Calculez le préfixe d'espacement commun de toutes les lignes déterminantes;
- Supprimez le préfixe des espaces communs de chaque ligne déterminante.
- Les séquences d'échappement dans le contenu sont interprétées. Les blocs de texte utilisent le même ensemble de séquences d'échappement que les chaînes de caractères littérales. Le traitement de ces derniers signifie que les échappements comme le font
\n
,\t
,\s
et\<eol>
n'affecte pas le traitement des espaces. (Deux nouvelles séquences d'échappement ont été ajoutées à l'ensemble dans le cadre de la JEP 368;\s
pour un espace explicite, et\<eol>
comme indicateur de continuation.)
Dans notre exemple XML, tous les espaces seront supprimés de la première et de la dernière ligne, et les deux lignes du milieu seront indentées de quatre espaces, car il y a cinq lignes déterminantes dans cet exemple - les quatre lignes contenant le code XML et la ligne contenant le délimiteur de fermeture - et les lignes sont toutes en retrait d'au moins autant d'espaces que la première ligne de contenu. Souvent, cette indentation est ce qui est attendu, mais parfois nous ne souhaitons pas supprimer toute l'indentation principale. Si, par exemple, nous voulions que le bloc entier soit indenté de quatre espaces, nous pourrions le faire en déplaçant le délimiteur de fermeture vers la gauche de quatre espaces :
void m() {
System.out.println("""
<person>
<firstName>Bob</firstName>
<lastName>Jones</lastName>
</person>
""");
}
Étant donné que la dernière ligne est également une ligne déterminante, le préfixe d'espaces communs est désormais la quantité d'espaces avant le délimiteur de fermeture dans la dernière ligne du bloc, et c'est la quantité qui est supprimée de chaque ligne, laissant le bloc entier indenté par quatre espaces. Nous pourrions également gérer l'indentation par programmation, via la méthode d'instance String::indent), qui prend une chaîne de plusieurs lignes (qu'elle provienne d'un bloc de texte ou non), et indente chaque ligne d'un nombre fixe d'espaces :
void m() {
System.out.println("""
<person>
<firstName>Bob</firstName>
<lastName>Jones</lastName>
</person>
""".indent(4));
}
Dans le cas extrême, si aucun effacement d'espace n'est souhaité, le délimiteur de fermeture peut être déplacé complètement vers la gauche :
void m() {
System.out.println("""
<person>
<firstName>Bob</firstName>
<lastName>Jones</lastName>
</person>
""");
}
Alternativement, nous pourrions obtenir le même effet en déplaçant tout le corps du bloc de texte vers la marge :
void m() {
System.out.println("""
<person>
<firstName>Bob</firstName>
<lastName>Jones</lastName>
</person>
""");
}
Ces règles peuvent sembler quelque peu compliquées au début, mais les règles ont été choisies pour équilibrer les diverses préoccupations concurrentes de vouloir pouvoir mettre en retrait le bloc de texte par rapport au programme environnant tout en ne générant pas de quantités variables d'espaces de tête accessoires et offrant un moyen facile pour ajuster ou désactiver la suppression des espaces blancs si l'algorithme par défaut n'est pas celui souhaité.
Expressions intégrées
Les chaînes littérales de Java ne prennent pas en charge l'interpolation d'expressions, comme le font certains autres langages; les blocs de texte non plus. (Dans la mesure où nous pourrions envisager cette fonctionnalité à un moment donné dans le futur, elle ne serait pas spécifique aux blocs de texte, mais appliquée également aux chaînes littérales.) Historiquement, les expressions de chaîne paramétrées ont été construites avec une concaténation de chaîne ordinaire (+
); en Java 5, String::format
a été ajouté pour prendre en charge la mise en forme des chaînes de style "printf".
En raison de l'analyse globale entourant les espaces, il peut être difficile d'obtenir le bon retrait lors de la combinaison de blocs de texte avec la concaténation de chaînes. Mais, un bloc de texte est évalué comme une chaîne ordinaire, nous pouvons donc toujours utiliser String::format
pour paramétrer l'expression de chaîne. De plus, nous pouvons utiliser la nouvelle méthode String::formated
, qui est une version d'instance de String::format
:
String person = """
<person>
<firstName>%s</firstName>
<lastName>%s</lastName>
</person>
""".formatted(first, last));
(Malheureusement, cette méthode ne peut pas également être appelée format
car nous ne pouvons pas surcharger les méthodes statiques et d'instance avec le même nom et les mêmes listes de paramètres.
Précédents et histoire
Alors que les chaînes littérales sont, dans un certain sens, une caractéristique "triviale", ils sont utilisés assez fréquemment pour que de petites irritations puissent s'additionner. Il n'est donc pas surprenant que le manque de chaînes multilignes ait été l'une des plaintes les plus courantes à propos de Java ces dernières années, et que de nombreux autres langages aient plusieurs formes de chaînes littérales pour prendre en charge différents cas d'utilisation.
Ce qui peut être surprenant, c'est le nombre de façons différentes dont une telle caractéristique est exprimée dans les langages populaires. Il est facile de dire "nous voulons des chaînes multi-lignes", mais lorsque nous étudions d'autres langages, nous trouvons une gamme étonnamment variée d'approches en termes de syntaxe et d'objectifs. (Et, bien sûr, un large éventail d'opinions de développeurs sur la "bonne" façon de le faire.) Bien qu'il n'y ait pas deux langages identiques, pour la plupart des fonctionnalités communes à un large éventail de langages (comme for
pour les boucles), il existe généralement quelques approches courantes parmi lesquelles les langages choisissent; il est inhabituel de trouver quinze interprétations différentes d'une fonctionnalité dans quinze langages, mais c'est exactement ce que nous avons trouvé en ce qui concerne les chaînes littérales multi-lignes et bruts.
Le tableau ci-dessous présente (certaines) des options pour les chaînes littérales dans différents langages. Dans chacun, le ...
est considéré comme le contenu de la chaîne littérale, qui peut ou non être traité pour les séquences d'échappement et les interpolations incorporées, xxx
représente un choix de l'utilisateur nonce qui est garanti de ne pas entrer en conflit avec le contenu de la chaîne, et ##
représente un nombre variable de symboles #
(qui peut être zéro.)
Language | Syntax | Notes |
---|---|---|
Bash | '...' | [span] |
Bash | $'...' | [esc] [span] |
Bash | "..." | [esc] [interp] [span] |
C | "..." | [esc] |
C++ | "..." | [esc] |
C++ | R"xxx(...)xxx" | [span] [delim] |
C# | "..." | [esc] |
C# | $"..." | [esc] [interp] |
C# | @"..." | |
Dart | '...' | [esc] [interp] |
Dart | "..." | [esc] [interp] |
Dart | '''...''' | [esc] [interp] [span] |
Dart | """...""" | [esc] [interp] [span] |
Dart | r'...' | [prefix] |
Go | "..." | [esc] |
Go | ... |
[span] |
Groovy | '...' | [esc] |
Groovy | "..." | [esc] [interp] |
Groovy | '''...''' | [esc] [span] |
Groovy | """...""" | [esc] [interp] [span] |
Haskell | "..." | [esc] |
Java | "..." | [esc] |
Javascript | '...' | [esc] [span] |
Javascript | "..." | [esc] [span] |
Javascript | ... |
[esc] [interp] [span] |
Kotlin | "..." | [esc] [interp] |
Kotlin | """...""" | [interp] [span] |
Perl | '...' | |
Perl | "..." | [esc] [interp] |
Perl | <<'xxx' | [here] |
Perl | <<"xxx" | [esc] [interp] [here] |
Perl | q{...} | [span] |
Perl | qq{...} | [esc] [interp] [span] |
Python | '...' | [esc] |
Python | "..." | [esc] |
Python | '''...''' | [esc] [span] |
Python | """...""" | [esc] [span] |
Python | r'...' | [esc] [prefix] |
Python | f'...' | [esc] [interp] [prefix] |
Ruby | '...' | [span] |
Ruby | "..." | [esc] [interp] [span] |
Ruby | %q{...} | [span] [delim] |
Ruby | %Q{...} | [esc] [interp] [span] [delim] |
Ruby | <<-xxx | [here] [interp] |
Ruby | <<~xxx | [here] [interp] [strip] |
Rust | "..." | [esc] [span] |
Rust | r##"..."## | [span] [delim] |
Scala | "..." | [esc] |
Scala | """...""" | [span] |
Scala | s"..." | [esc] [interp] |
Scala | f"..." | [esc] [interp] |
Scala | raw"..." | [interp] |
Swift | ##"..."## | [esc] [interp] [delim] |
Swift | ##"""..."""## | [esc] [interp] [delim] [span] |
Légende :
- esc. Un certain degré de traitement de séquences d'échappement, où les échappements sont généralement dérivés du style C (par exemple,
\n
); - interp. Certains prennent en charge l'interpolation de variables ou d'expressions arbitraires.
- span. Les chaînes multi-lignes peuvent être exprimées en couvrant simplement plusieurs lignes source.
- here. Un "here-doc", où les lignes suivantes, jusqu'à une ligne qui ne contient que le nonce sélectionné par l'utilisateur, sont traitées comme le corps de la chaîne littérale.
- prefix. Le formulaire de préfixe est valide avec toutes les autres formes de chaîne littérale et a été omis par souci de concision.
- delim. Le délimiteur est personnalisable dans une certaine mesure, que ce soit en incluant un nonce (C ++), un nombre variable de caractères
#
(Rust, Swift), ou en échangeant des accolades pour d'autres crochets assortis (Ruby). - strip. Un certain degré de suppression de l'indentation accessoire est pris en charge.
Bien que ce tableau donne une vision de la diversité des approches des chaînes littérales, il ne fait qu'effleurer la surface, car la variété des subtilités dans la façon dont les langages interprètent les chaînes littérales sont trop variées pour être capturées sous une forme aussi simple. Alors que la plupart des langages utilisent un langage d'échappement inspiré de C, ils varient exactement en ce qui concerne les échappements qu'ils prennent en charge, si et comment ils prennent en charge les échappements unicode (par exemple, \unnnn
), et si les formulaires qui ne prennent pas en charge le langage d'échappement complet prend toujours en charge une forme limitée d'échappement pour les caractères délimiteurs (comme l'utilisation de deux guillemets pour un guillemet incorporé au lieu de terminer la chaîne.) Le tableau omet également un certain nombre d'autres formes (comme les divers préfixes en C++ pour contrôler le codage de caractères) pour plus de concision.
L'axe de variation le plus évident d'un langage à l'autre est le choix des délimiteurs et la manière dont les différents délimiteurs signalent différentes formes de chaînes littérales (avec ou sans échappements, lignes simples ou multiples, avec ou sans interpolation, choix des encodages de caractères, etc.) mais la lecture entre les lignes, nous pouvons voir comment ces choix syntaxiques reflètent souvent des différences philosophiques sur la conception du langage - comment équilibrer les différents objectifs tels que la simplicité, l'expressivité et la commodité de l'utilisateur.
Sans surprise, les langages de script (bash, Perl, Ruby, Python) ont fait du "choix de l'utilisateur" leur première priorité, avec de nombreuses formes de littéraux qui peuvent varier de manière non orthogonale (et souvent, de multiples façons d'exprimer la même chose.) Mais en général, les langages sont omniprésents dans la façon dont elles encouragent les utilisateurs à penser aux chaînes littérales, au nombre de formes qu'ils exposent et à l'orthogonalité de ces formes. Nous voyons également plusieurs philosophies sur les chaînes qui s'étendent sur plusieurs lignes. Certains terminateurs de ligne (comme Javascript et Go) ne sont qu'un autre caractère, permettant à toutes les formes de chaînes littérales de s'étendre sur plusieurs lignes, certains (tels que C ++) les traitent comme un cas spécial de chaînes "brutes", et d'autres (tels que Kotlin ) qui divisent les chaînes en "simples" et "complexes" et placent des chaînes multilignes dans la catégorie "complexe", et d'autres offrent tellement d'options qu'elles défient même ces classifications simples. De même, leur interprétation de la "chaîne brute" varie. Le vrai aspect brut nécessite une certaine forme de délimiteur contrôlable par l'utilisateur (comme C++, Swift et Rust), bien que d'autres appellent leurs chaînes "brutes" mais réservent toujours une forme d'échappement pour leur délimiteur de fermeture (fixe).
Malgré l'éventail d'approches et d'opinions, du point de vue de l'équilibre entre la conception fondée sur des principes et l'expressivité, il y a clairement un «gagnant» de cette enquête : Swift. Il parvient à prendre en charge l'échappement, l'interpolation et le véritable aspect brut avec un mécanisme unique et flexible (dans les variantes à une et plusieurs lignes). Il ne devrait pas être surprenant que le langage le plus récent du groupe ait l'approche la plus propre, car elle avait le bénéfice du recul et pouvait tirer les leçons des succès et des erreurs des autres. (L'innovation clé ici est que le délimiteur d'échappement varie au même rythme que le délimiteur de chaîne, évitant d'avoir à choisir entre les modes "cuit" et "cru", tout en partageant le langage d'échappement sur toutes les formes de chaîne littérale - une approche qui garantit l'éloge du "évident avec le recul".) Bien que Java ne puisse pas adopter l'approche Swift en gros en raison des contraintes de langage existantes, l'approche de Java s'est inspirée autant que possible du bon travail de la communauté Swift - et a laissé de la place pour en prendre davantage à l'avenir.
Le chemin empruntée
Les blocs de texte n'étaient pas la première itération de cette fonctionnalité; la première itération était des chaînes littérales bruts (raw string literals). Comme les chaînes brutes de Rust, il a utilisé un délimiteur de taille variable (n'importe quel nombre de caractères quote inverse) et n'a pas du tout interprété le contenu. Cette proposition a été retirée après avoir été entièrement conçue et prototypée, car il a été jugé que, même si elle était suffisamment solide, elle semblait trop «clouée sur le côté» - elle avait trop peu de points communs avec les chaînes littérales traditionnelles, et donc, si nous voulions étendre la fonctionnalité à l'avenir, il n'y avait pas de moyen de les étendre ensemble. (En raison de la cadence de release rapide, cela n'a retardé la fonctionnalité que de six mois et a abouti à une fonctionnalité nettement meilleure.)
Une objection majeure à l'approche de la JEP 326 est que les chaînes brutes fonctionnent différemment à tous égards des chaînes littérales traditionnelles; différents délimiteurs, variant par rapport aux délimiteurs fixes, à ligne unique ou à lignes multiples, avec échappement ou sans échappement. Invariablement, quelqu'un voudra une combinaison de choix différente, et il y aura des appels à des formes plus différentes, nous conduisant sur la route que Bash a prise. En plus de cela, il n'a rien de fait pour résoudre le problème de "l'indentation accessoire", qui allait évidemment être une source de fragilité dans les programmes Java. Tirant parti de cette expérience, les blocs de texte partagent beaucoup plus avec des chaînes littérales traditionnelles (syntaxe de délimitation, langage d'échappement), ne variant que dans un aspect crucial - que la chaîne soit une séquence de caractères unidimensionnelle ou un bloc de texte bidimensionnel .
Guide de style
Jim Laskey et Stuart Marks, de l'équipe Java d'Oracle, ont publié un guide du programmeur décrivant les détails et les recommandations de style pour les blocs de texte.
Utilisez des blocs de texte lorsqu'il améliore la clarté du code. La concaténation, les retours à la ligne échappés et les délimiteurs de guillemets échappés obscurcissent le contenu d'une chaîne littérale; les blocs de texte sont "hors de cette problématique", donc le contenu est plus évident, mais il est syntaxiquement plus lourd que les chaînes littérales traditionnelles. Utilisez-les là où les avantages paient pour les coûts supplémentaires; si une chaîne tient sur une seule ligne et n'a pas de nouvelle ligne échappée, il est probablement préférable de s'en tenir aux chaîne littérales traditionnelles.
Évitez les blocs de texte en ligne dans les expressions complexes. Bien que les blocs de texte soient des expressions sous forme de chaîne et puissent donc être utilisés partout où une chaîne est attendue, il n'est pas toujours préférable d'imbriquer des blocs de texte dans des expressions complexes; il est parfois préférable de les extraire dans une variable distincte. Dans l'exemple suivant, le bloc de texte rompt le flux du code lors de la lecture, forçant les lecteurs à changer mentalement de vitesse :
String poem = new String(Files.readAllBytes(Paths.get("jabberwocky.txt")));
String middleVerses = Pattern.compile("\\n\\n")
.splitAsStream(poem)
.match(verse -> !"""
’Twas brillig, and the slithy toves
Did gyre and gimble in the wabe;
All mimsy were the borogoves,
And the mome raths outgrabe.
""".equals(verse))
.collect(Collectors.joining("\n\n"));
Si nous affectons le bloc de texte dans sa propre variable, il est plus facile pour les lecteurs de suivre le déroulement du calcul:
String firstLastVerse = """
’Twas brillig, and the slithy toves
Did gyre and gimble in the wabe;
All mimsy were the borogoves,
And the mome raths outgrabe.
""";
String poem = new String(Files.readAllBytes(Paths.get("jabberwocky.txt")));
String middleVerses = Pattern.compile("\\n\\n")
.splitAsStream(poem)
.match(verse -> !firstLastVerse.equals(verse))
.collect(Collectors.joining("\n\n"));
Évitez de mélanger les espaces et les tabulations dans l'indentation d'un bloc de texte. L'algorithme de suppression de l'indentation accessoire calcule un préfixe d'espace blanc commun et, par conséquent, fonctionnera toujours si les lignes sont indentées de manière cohérente avec une combinaison d'espaces et de tabulations. Cependant, cela est évidemment fragile et sujet aux erreurs, il est donc préférable d'éviter de les mélanger - utilisez l'un ou l'autre.
Alignez les blocs de texte avec le code Java voisin. Étant donné que les espaces accessoires sont automatiquement supprimés, nous devons en profiter pour faciliter la lecture du code. Alors que nous pourrions être tentés d'écrire :
void printPoem() {
String poem = """
’Twas brillig, and the slithy toves
Did gyre and gimble in the wabe;
All mimsy were the borogoves,
And the mome raths outgrabe.
""";
System.out.print(poem);
parce que nous ne voulons pas d'indentation principale dans nos chaînes, la plupart du temps nous devons écrire :
void printPoem() {
String poem = """
’Twas brillig, and the slithy toves
Did gyre and gimble in the wabe;
All mimsy were the borogoves,
And the mome raths outgrabe.
""";
System.out.print(poem);
}
car cela impose moins de charge cognitive au lecteur.
Ne vous sentez pas obligé d'aligner le texte avec le délimiteur d'ouverture. Nous pouvons choisir d'aligner le contenu du bloc de texte avec le délimiteur d'ouverture:
String poem = """
’Twas brillig, and the slithy toves
Did gyre and gimble in the wabe;
All mimsy were the borogoves,
And the mome raths outgrabe.
""";
Cela peut sembler intéressant, mais peut être encombrant si les lignes sont longues ou si le délimiteur commence loin dans la marge de gauche, car maintenant le texte restera complètement dans la marge de droite. Mais cette forme d'indentation n'est pas requise; nous pouvons utiliser n'importe quelle indentation de continuation, tant que nous le faisons de manière cohérente :
String poem = """
’Twas brillig, and the slithy toves
Did gyre and gimble in the wabe;
All mimsy were the borogoves,
And the mome raths outgrabe.
""";
Lorsqu'un bloc de texte contient une triple double quote intégrée, n'échappez que la première double quote. Bien qu'il soit possible d'échapper à chaque double quote, il n'est pas nécessaire et cela interfère inutilement avec la lisibilité; seul l'échappement de la première double quote est nécessaire :
String code = """
String source = \"""
String message = "Hello, World!";
System.out.println(message);
\""";
""";
Envisagez de diviser les très longues lignes avec \. Avec les blocs de texte, nous avons deux nouvelles séquences d'échappement, \s
(pour un espace littéral) et \<newline>
(un indicateur de continuation de ligne.) Si nous avons des littéraux avec des lignes très longues, nous pouvons utiliser \<newline>
pour mettre un saut de ligne dans le code source mais qui est supprimé pendant le traitement d'échappement au moment de la compilation de la chaîne.
Synthèse
Les littéraux de chaîne dans les programmes Java ne sont pas limités aux chaînes courtes comme "oui"et "non"; ils correspondent souvent à des «programmes» entiers dans des langages structurés tels que HTML, SQL, XML, JSON ou même Java. Être en mesure de préserver la structure bidimensionnelle de ce programme intégré, sans avoir à l'étouffer avec des caractères d'échappement et d'autres intrusions linguistiques, est moins sujet aux erreurs et se traduit par des programmes plus lisibles.
A propos de l'auteur
Brian Goetz est l'architecte du langage Java chez Oracle et a été le responsable des spécifications pour la JSR-335 (Expressions Lambda pour le langage de programmation Java). Il est l'auteur du best seller Java Concurrency in Practice, et a été fasciné par la programmation depuis que Jimmy Carter était président.