用Java代码在多行上写字符串文字,并可能用空格格式化,这对许多开发人员来说很烦人。 例如,为了清楚地用几行代码格式化SQL语句,通常使用+运算符并在末尾手动插入换行符(清单1)。
要查看大多数语言已经包含正确格式化字符串文字的功能,只需查看Groovy,Scala和Kotlin。 其他高级编程语言(如C#,Swift或Python)绝不逊色。 有了如此众多的模型,就有可能适应Java实现的最佳属性。 从最初用于Java 12的原始字符串文字的JEP 326中可以看出。社区有很多评论和反对意见,于是撤回了提案 。
JEP 355已经考虑了许多注释,现在允许从Java 13开始的开发人员轻松地在代码中定义多行字符串。 该功能首先在JDK 13中作为预览功能引入-因此,如果要使用它,则必须使用--enable-preview
标志来编译代码。
String statement = "SELECT\n" +
" c.id,\n" +
" c.firstname,\n" +
" c.lastName,\n" +
" c.customernumber\n" +
"FROM customers c;";
JEP 326:原始字符串
为了更好地对文本块的某些设计决策进行分类,值得一提的是撤回的JEP 326 。 它以JDK 12为目标,目的是在Java中引入原始字符串,即可以跨越多行并且不解释转义序列的字符串。
为了尽可能灵活并能够独立于所包含的文本起作用,建议使用任意数量的反引号(`)作为边界序列。 如果字符串本身包含反引号,则边界序列必须至少包含两个。 如果序列包含两个连续的反引号,则边界序列将至少具有三个,依此类推。
还请参见:
在宣布将撤消JEP的声明中,Brian Goetz提到了导致该决定的社区批评。 许多开发人员担心边界序列中可变数量的字符可能会使人和开发环境感到困惑。 也有人批评说,反引号将少数未使用的定界符之一引入将“用完”并且不能用于将来的功能的代码中。
此外,具有建议功能的任何多行字符串文字都必须自动成为原始字符串的事实导致撤销JEP 236的决定。JEP355中包含的方法明确基于此过程的发现。
分隔符和格式
多行字符串文字由三个引号(“””)组成。 开头字符序列只能可选地后面跟空格和强制换行符。 文字的实际内容从下一行代码(清单2)开始。 单引号和双引号可能会出现在文字中而没有转义。 如果文字本身包含三个引号,则必须至少转义两个引号。
String correctString = """ // First character only in next line, correct
{
"type": "json",
"content": "sampletext"
}
""";
String incorrectString = """{ // Characters after delimiter sequence, incorrect
"type": "json",
"content": "sampletext"
}
""";
多行字符串文字在编译时进行处理。 编译器始终根据相同的模式进行处理:字符串中的所有换行符都首先转换为与操作系统无关的换行符(\ u000A)。 在文本中以\ n或\ r这样的转义字符明确输入的序列不包括在其中。
在第二步中,删除了由于代码格式化而产生的空白。 它用三个引号的位置标记。 这使您可以在代码中放置文本块,使其与其余的代码格式匹配(清单3)。
对于Scala和Kotlin,必须在源文本中将多行文字写为左对齐而没有空格,或者必须通过字符串操作将它们从空格中释放出来。 另一方面,Java强烈基于Swift的过程,因此在运行时无需清理字符串。
最后,将解释和解析文本中的所有转义序列。 编译后,无论是否已将其定义为多行字符串,都不再可能找到在代码中如何定义字符串。
String string1 = """
{
"type": "json",
"content": "sampletext"
}
""";
// Content of string1.
// The left margin is marked by | for illustration purposes.
//|{
//| "type": "json",
//| "content": "sampletext"
//|}
String string2 = """
{
"type": "json",
"content": "sampletext"
}
""";
// Content of string2.
// The left margin is marked by | for illustration purposes.
//| {
//| "type": "json",
//| "content": "sampletext"
//| }
用例
由于文本块为开发人员提供了新的可能性,因此在某些情况下,代码可以编写得更清晰和/或更易读。 如前所述,例如现在可以更清晰地以Java代码显示SQL语句。 由于文本块可以在允许使用常规字符串文字的任何地方使用,因此,这甚至适用于具有JQL或本机SQL的命名查询(清单4),它们在注释中定义。
@Entity
@NamedQuery(name = "findByCustomerNumberAndCity",
query = """
from Customer c
where c.customerNo = :customerNo
and c.city = :city
""")
public class CustomerEntity {
String customerNo;
String city;
public String getCustomerNo() {
return customerNo;
}
public void setCustomerNo(String customerNo) {
this.customerNo = customerNo;
}
}
如果要使用字符串文字作为模板,可以找到更多有用的应用程序。 这对于要用于测试REST服务的单元测试中的JSON有效负载很有用,或者对于在服务器端准备HTML代码段很有用(清单5)。
String htmlContent = """
<div>
<h2>My header</h2>
<ul>
<li>An entry</li>
<li>Another entry</li>
</ul>
</div>
""";
此外,无论是在旧的Rhino引擎还是在GraalVM的上下文中,文本块都极大地简化了多语言功能的使用。 如果开始时提到的构造必须与字符串连接和手动换行符一起使用,则用文字定义JavaScript代码将很难读取和维护,如清单6所示。
ScriptEngine engine = new ScriptEngineManager().getEngineByName("js");
Object result = engine.eval("""
function add(int1, int2) {
return int1 + int2;
}
add(1, 2);""");
新方法
JEP 355在String类中添加了三个新的实例方法:formatted,stripIndent和translateEscapes。 格式化是一种辅助方法,使使用多行文字更容易,而stripIndent和translateEscapes表示其解释的最后两个编译步骤。
如前所述,多行字符串非常适合在代码中定义模板。 为了用值填充字符串模板中的占位符,String类中已经存在静态方法格式。 由于多行定义的字符串文字在单行文字的使用上没有区别,因此当然也可以使用此方法对其进行格式化。
为了为文本块提供更清晰的方法,已将formatted
的新实例方法添加到String类,其行为类似于静态方法format
。 结果也是一个使用占位符格式化的字符串,但是与文本块结合使用时,它会导致代码整齐(清单7)。
String.format(„Hallo, %s, %s dich zu sehen!“, „Duke“, „schön“);
String xmlString = """
<customer>
<no>%s</no>
<name>%s</name>
</customer>
""".formatted("12345", "Franz Kunde");
stripIndent方法删除所有行共有的多行字符串前面的空白,即,将整个文本向左移动而不更改格式。 translateEscapes方法解释字符串中包含的所有转义序列。 清单8再次阐明了这种行为。
// The left margin is marked by | for illustration purposes.
String example = " a\n b\\n c";
System.out.println(example);
//| a
//| b\n c
System.out.println(example.stripIndent());
//|a
//| b\n c
System.out.println(example.translateEscapes());
//| a
//| b
//| c
System.out.println(example.stripIndent().translateEscapes());
//|a
//| b
//| c
结论
使用JEP 355,引入多行字符串文字的方式可以使它们在声明和格式化中快速感觉自然。 开发人员从其他编程语言中获得的经验中受益匪浅,他们选择了一种对字符串进行后处理的方法,例如,去除烦人的不必要空白,同时允许对源代码进行可读的格式化。
简介是通过String类中的三个新方法完成的:formatted,stripIndent和translateEscapes。 它们不仅使新的文本块更易于使用,而且还可以用于格式化其他来源的字符串,删除缩进或解释转义序列。
就个人而言,我非常期待将文本块作为一种全面的功能引入,并且几乎每天都注意到我目前有多少缺失。