在从Java 12中删除类似功能之后,Java 13最终支持多行字符串文字。
本文示例代码,详见 github
https://github.com/tudan110/java-13-training
Java中的字符串文字
在Java中声明字符串文字很容易:
String myString = "Behold, a string literal!";
没什么新鲜的吧?然而,传统的字符串文字有一些不方便的限制。
首先,整个文字只需要一行。这对于简单的短字符串是可以的,但对于较长的字符串,您需要求助于连接多个字符串文字。如果您还想包含换行符,则需要添加\n
。
String myString = "This is my string\n" +
"which I want to be \n" +
"on multiple lines.";
这很麻烦且难以阅读。另一个问题是各种字符无法直接表示,但需要进行转义,例如"
或\
。这使您的字符串更难以阅读和使用。
String path = "Open \"C:\\Program Files\\Java\\jdk1.8.0_152\"";
在正则表达式等情况下,这尤其混乱。如何处理这个问题呢?
其他JVM语言
通常,Java推出的功能已经很晚了,这种功能在许多其他语言中已经很常见,甚至在JVM上也是如此。Kotlin,Scala或Groovy已经使用"""
符号而不是通常来支持多行字符串文字"
。请看Kotlin中的以下示例。
val text = """
This is my cool string,
which I want to span
multiple lines.
It can have backslash \
and even double-quotes "
"""
你可以注意到,它不仅是多线的,而且你不再需要逃避你的角色了。这称为Raw String literal,这意味着所有字符都按原样解释,并且不需要转义。
Java中的原始字符串文字
在Java 12中,最初提议包括Raw String Literals(JEP-326)。这种行为与上面Kotlin中的例子非常相似。但它不使用"""
其他JVM语言,而是使用反引号```表示法。这个概念略微不同寻常,你可以在最后使用任意数量的反引号和相同数量的反引号。这样,无论有多少连续的反推都存在,都没有必要在弹簧文字中逃避反推。
`This uses a single backtick`
``This can contain a backtick `, see?``
```I can use any number of backticks```
原始字符串文字的一个问题通常是缩进。多行原始字符串文字中的每个字符都被解释为 - 包括缩进,它应该只是为了更好的代码可读性而不是文字本身的一部分。请考虑以下示例:
public static void main(String[] args) {
String myString =
`This is my string
which I want to be
on multiple lines.`;
System.out.println(myString);
}
如果你打印它,除了第一行之外的所有行都会有缩进。
This is my string
which I want to be
on multiple lines.
您可能需要手动修复此问题,可能需要使用新提出的String方法align()
和indent()
。不是很实用,但如果使用原始字符串,这是你必须忍受的东西。
您可以在以下文章中阅读有关原始Raw String Literals提案的更多信息:
Java Raw String Literals
Java最终支持原始字符串。它们可以跨越多行,您不需要转义特殊字符。对正则表达式特别有用。
然而,在 Java 12 的最终版本发布之前,该提议已被删除,现在已被淘汰。
虽然我们可以预期,对于任何语言功能,都会有一个非常重要的“我会更喜欢它不同”的反馈,在回顾我们收到的反馈时,我不再相信我们已经达到了正确的设置在复杂性和表现力之间进行权衡,或者我们已经探索了足够的设计空间,以确信当前的设计是我们能做的最好的。通过撤销,我们可以继续改进设计,探索更多选项,并瞄准实际满足预览功能流程(JEP 12)要求的预览。
Java中的多行字符串文字
撤回原始提案后,讨论重新开始,并产生了一个全新的提案,该提案将作为预览功能在Java 13中引入。它叫做JEP 355:文本块(预览)。
它不应该是Raw String Literals的实现,而是多行String文字,它仍然允许转义,尽管你不需要使用它们。最重要的是,没有必要处理所有缩进问题,这是Raw String Literals所必需的。
预览功能
文本块仅作为Java 13中的预览功能提供。这是什么意思?
预览语言或VM功能是Java SE平台的一项新功能,该功能已完全指定,完全实现且非常无常。它在JDK功能版本中提供,可根据实际使用情况激发开发人员的反馈; 这可能会导致它在未来的Java SE平台中成为永久性的。
在下一个JDK功能发布之前,将评估该功能的“真实世界”优势和劣势,以确定该功能是否在Java SE平台中具有长期作用,如果是,则是否需要优化。因此,该特征可以被授予最终和永久状态(有或没有改进),或者经历进一步的预览时段(有或没有改进),或者被移除。
这些功能在JDK中提供,但默认情况下不启用。您需要明确允许它们使用它们。毋庸置疑,它不是用于生产用途,而是用于评估和实验,因为它可能在将来的版本中被删除或大量更改。
您需要下载JDK 13。在IntelliJ中,转到File → Project Structure
并确保在Project SDK下选择了JDK 13。要将文本块作为预览功能启用,请务必选择13 (Preview) - Text blocks
下方Project language level
。
手动构建应用程序时,您需要通过提供以下参数来指定应打开预览功能javac
:
javac --release 13 --enable-preview ...
那是编译时间。在运行时,您只需提供--enable-preview
java --enable-preview ...
文本块
与拒绝的Raw String Literals不同,Text Blocks被三个双引号括起来"""
,与Groovy或Kotlin中的相同。它与普通的字符串文字和其他JVM语言更加同步。
String myBlock = """
line 1
line 2
line 3
"""
Text Block和String literal之间没有运行时差异。两者都会产生String的实例。如果它们具有相同的值,它们将像往常一样被实例化并最终成为同一个实例。你可以使用String literal,也可以使用Text Block。
处理
与常规字符串文字不同,编辑器会在三个步骤中处理文本块:
- 线端标准化
- 删除了多余的空格
- 转义的字符被解释
行结束归一化
Windows和基于UNIX的操作系统具有不同的字符来表示行结尾。
Windows使用回车符\r
和换行符\n
,而基于Unix的系统仅使用换行符。问题是文本块直接使用源代码中的新行字符,而不是使用\n
常规字符串文字。这意味着在Unix上创建的源代码在Windows上编译时会有不同行结尾的字符串。字符串看起来与肉眼相同,但会有不同的行结尾。
为了防止这种情况,Java编译器获取文本块中的所有行结尾并将它们规范化为换行符\n
。重要的是,这是在评估转义字符之前完成的。这意味着如果您明确需要使用Carriage Return \r
,则可以在行结束规范化之后进行评估,并且不会受到影响。
缩进删除
还记得Raw String Literals和缩进的例子吗?原始字符串文字解释所有字符,包括缩进。因此,只是为了使您的源代码更容易阅读的空白实际上成为您的字符串的一部分。在绝大多数情况下,这不是期望的行为。
幸运的是,Java编译器在编译文本块时删除了不需要的空格。
- 所有尾随空格都从行尾删除。
- 从每行的开头删除前导公共空格。
这究竟是什么意思?我们来看看以下代码:
public static void main(String[] args) {
String html = """
<html>
<body>
<p>Hello, world</p>
</body>
</html>
""";
}
HTML代码段包含大量的空白,但它并不真正属于它。它只是使它在源文件中很好地对齐。重要的是块内的相对压痕。
换句话说:如果剪切中的每一行都以22个空格开头,我们可以忽略它们。这22个空格是常见的空格前缀,可以忽略,只应保留其中的空格。
让我们用.
。替换公共前缀。所有这些空间都将被丢弃。只有标记的空格-
才会保留,因为它们超出了常用的空格前缀。
String html = """
......................<html>
......................--<body>
......................----<p>Text Blocks are awesome!</p>
......................--</body>
......................</html>
......................""";
结果将是:
<html>
<body>
<p>Text Blocks are awesome!</p>
</body>
</html>
请注意,在此步骤中仅删除直接空格。如果你有一个转义字符形式的空格,如\n
或\t
,它将不会被删除。
逃离
文本块不是原始字符串,您仍然可以使用转义。但是,您不需要为最常见的打扰。
\n
不再需要新行,因为文本块本质上是多行的。- 您不需要转义
"
双引号,因为它们不再标记字符串文字结尾
String myBlock = """
First line
Second Line with " quotes
"""
由于允许引用,您在技术上可以包括\n
和\"
,但它没有必要,因此不鼓励。你仍然需要逃避斜线\\
。但总的来说,文本块涉及的逃避比旧的String文字少得多。可以在字符串文字中使用的所有转义序列也可以用于文本块。检查Java规范以获取完整列表。
将转义字符解释为三个步骤中的最后一个很重要,因为转义不受行结束规范化和空格移除的影响。
新的字符串方法
作为Text Blocks提案的一部分,有三种新的String
类方法。
translateEscapes()
- 转换字符串中的转义序列,除了unicode序列。stripIndent()
- 从每行的开头剥去常见的空格。formatted(Object... args)
- 便捷方法,相当于String.format(string,args)
结论
- 文本块提供了一种使用多行字符串文字的便捷方式。
- 要创建文本块,只需用
"""
。 - 您可以在任何可以使用字符串文字的地方使用文本块
- 行结尾标准化为LF
- 从每行的开头和结尾剥去额外的空格。仅保留相对缩进。
- 它是Java 13中的预览功能,需要明确启用
- Raw String literals提案已从Java 12中撤回; 文本块后来被引入