以下代码需要使用Java 13 才能运行,并且需要设置语言级别为13(Preview).IDEA中File-Project Structure-Project language level-选择 13(Preview)-Switch expressions,text blocks.
以下内容机翻整理自OpenJDK官方:JEP 355,稍有改动.
HTML示例
// HTML: Java 13 之前. String html = "<html>\n" + " <body>\n" + " <p>Hello, world</p>\n" + " </body>\n" + "</html>\n"; System.out.println(html); // HTML: Java 13 html = """ <html> <body> <p>Hello, world</p> </body> </html> """; System.out.println(html); System.out.println("-------------------------"); /// ---------------------
SQL示例
// SQL: Java 13之前. String query = "SELECT `EMP_ID`, `LAST_NAME` FROM `EMPLOYEE_TB`\n" + "WHERE `CITY` = 'INDIANAPOLIS'\n" + "ORDER BY `EMP_ID`, `LAST_NAME`;\n"; System.out.println(html); // SQL: Java 13 query = """ SELECT `EMP_ID`, `LAST_NAME` FROM `EMPLOYEE_TB` WHERE `CITY` = 'INDIANAPOLIS' ORDER BY `EMP_ID`, `LAST_NAME`; """; System.out.println(html);
编译时处理
文本块是type 的常量表达式String,就像字符串文字一样。但是,与字符串文字不同,Java编译器通过三个不同的步骤处理文本块的内容:
- 内容中的行终止符转换为LF(\u000A)。这种转换的目的是在跨平台移动Java源代码时遵循最小惊喜的原则。
- 删除了内容周围附带的空白,以匹配Java源代码的缩进。
- 内容中的转义序列被解释。作为最后一步执行解释意味着开发人员可以编写转义序列,例如\n无需通过早期步骤对其进行修改或删除。
处理后的内容class作为CONSTANT_String_info常量池中的一项记录在文件中,就像字符串文字的字符一样。该class文件不会记录CONSTANT_String_info条目是从文本块还是字符串文字派生的。
在运行时,String像字符串文字一样,将文本块评估为的实例。的实例String被来源于文本块是从字符串常量衍生的实例没有区别。具有相同处理内容的两个文本块String由于interning会引用相同的instance ,就像字符串文字一样。
转义
html = """ <html>\r <body>\r <p>Hello, world</p>\r </body>\r </html>\r """; System.out.println(html);
在文本块中使用”双引号.是合法的:
String story = """ "When I use a word," Humpty Dumpty said, in rather a scornful tone, "it means just what I choose it to mean - neither more nor less." "The question is," said Alice, "whether you can make words mean so many different things." "The question is," said Humpty Dumpty, "which is to be master - that's all." """; System.out.println(story);
三个双引号,必须进行转义,防止模糊结束定义符:
String code = """ String text = \""" A text block inside a text block \"""; """; System.out.println(code);
文本块的串联
可以在使用字符串文字的任何地方使用文本块:
code = "public void print(Object o) {" + """ System.out.println(Objects.toString(o)); } """; System.out.println(code);
但是,涉及文本块的串联可能会变得很笨拙。以以下文本块为起点:
code = """ public void print(Object o) { System.out.println(Objects.toString(o)); } """;
假设需要更改它,以便类型o来自变量。使用串联,包含尾随代码的文本块将需要从新行开始。
不幸的是,如下所示,在程序中直接插入换行符会导致类型和开头的文本之间存在很大的空白o:
String type = "test"; code = """ public void print(""" + type + """ o) { System.out.println(Objects.toString(o)); } """; System.out.println(code);
可以手动删除空格,但这会损害引用代码的可读性:
code = """ public void print(""" + type + """ o) { System.out.println(Objects.toString(o)); } """; System.out.println(code);
较干净的替代方法是使用String::replace或String::format,如下所示:
code = """ public void print($type o){ System.out.println(Objects.toString(o)); } """.replace("$type", type); System.out.println(code); String name = "name"; String age = "age"; String address = "address"; String phone = "phone"; String height = "height"; code = """ public void print($type o){ String name="$name"; String age="$age"; String address="$address"; String phone="$phone"; String height="$height"; System.out.println(Objects.toString(o)); } """.replace("$type", type) .replace("$name", name) .replace("$age", age) .replace("$address", address) .replace("$phone", phone) .replace("$height", height); System.out.println(code); code = String.format(""" public void print(%s o) { System.out.println(Objects.toString(o)); } """, type); System.out.println(code);
另一种选择是引入新的实例方法String::formatted,该方法可以按如下方式使用:
String source = """ public void print(%s object) { System.out.println(Objects.toString(object)); } """.formatted(type); System.out.println(source);
附加方法
将添加以下方法来支持文本块:
- String::stripIndent():用于从文本块内容中去除附带的空白
- String::translateEscapes():用于翻译转义序列
- String::formatted(Object… args):简化文本块中的值替换
其实相对于Python 3的多行字符串(Java的文本块),Java的String::replace,String:format,String::formatted,还是复杂了很多.例如,下面的代码:
String name = "name"; String age = "age"; String address = "address"; String phone = "phone"; String height = "height"; code = """ public void print($type o){ String name="$name"; String age="$age"; String address="$address"; String phone="$phone"; String height="$height"; System.out.println(Objects.toString(o)); } """.replace("$type", type) .replace("$name", name) .replace("$age", age) .replace("$address", address) .replace("$phone", phone) .replace("$height", height); System.out.println(code);
在Python 3中:
name = "name" age = "age" address = "address" phone = "phone" height = "height" code = """ public void print(Object o){{ String name="{0}"; String age="{1}"; String address="{2}"; String phone="{3}"; String height="{4}"; System.out.println(Objects.toString(o)); }} """.format(name,age,address,phone,height); print(code);
这样替换文本,既方便又省事..
完整源码
public class JavaTextBlockDemo { public static void main(String[] args) { // HTML: Java 13 之前. String html = "<html>\n" + " <body>\n" + " <p>Hello, world</p>\n" + " </body>\n" + "</html>\n"; System.out.println(html); // HTML: Java 13 html = """ <html> <body> <p>Hello, world</p> </body> </html> """; System.out.println(html); System.out.println("-------------------------"); /// --------------------- // SQL: Java 13之前. String query = "SELECT `EMP_ID`, `LAST_NAME` FROM `EMPLOYEE_TB`\n" + "WHERE `CITY` = 'INDIANAPOLIS'\n" + "ORDER BY `EMP_ID`, `LAST_NAME`;\n"; System.out.println(html); // SQL: Java 13 query = """ SELECT `EMP_ID`, `LAST_NAME` FROM `EMPLOYEE_TB` WHERE `CITY` = 'INDIANAPOLIS' ORDER BY `EMP_ID`, `LAST_NAME`; """; System.out.println(html); System.out.println("-------------------------"); /// --------------------- // h2. 转义 html = """ <html>\r <body>\r <p>Hello, world</p>\r </body>\r </html>\r """; System.out.println(html); // 使用"双引号.是合法的 String story = """ "When I use a word," Humpty Dumpty said, in rather a scornful tone, "it means just what I choose it to mean - neither more nor less." "The question is," said Alice, "whether you can make words mean so many different things." "The question is," said Humpty Dumpty, "which is to be master - that's all." """; System.out.println(story); // 三个双引号,必须进行转义,防止模糊结束定义符. String code = """ String text = \""" A text block inside a text block \"""; """; System.out.println(code); // h2.文本块的串联 // 可以在使用字符串文字的任何地方使用文本块. code = "public void print(Object o) {" + """ System.out.println(Objects.toString(o)); } """; System.out.println(code); // 但是,涉及文本块的串联可能会变得很笨拙。以以下文本块为起点: code = """ public void print(Object o) { System.out.println(Objects.toString(o)); } """; // 假设需要更改它,以便类型o来自变量。使用串联,包含尾随代码的文本块将需要从新行开始。 // 不幸的是,如下所示,在程序中直接插入换行符会导致类型和开头的文本之间存在很大的空白o: String type = "test"; code = """ public void print(""" + type + """ o) { System.out.println(Objects.toString(o)); } """; System.out.println(code); // 可以手动删除空格,但这会损害引用代码的可读性: code = """ public void print(""" + type + """ o) { System.out.println(Objects.toString(o)); } """; System.out.println(code); // 较干净的替代方法是使用String::replace或String::format,如下所示: code = """ public void print($type o){ System.out.println(Objects.toString(o)); } """.replace("$type", type); System.out.println(code); String name = "name"; String age = "age"; String address = "address"; String phone = "phone"; String height = "height"; code = """ public void print($type o){ String name="$name"; String age="$age"; String address="$address"; String phone="$phone"; String height="$height"; System.out.println(Objects.toString(o)); } """.replace("$type", type) .replace("$name", name) .replace("$age", age) .replace("$address", address) .replace("$phone", phone) .replace("$height", height); System.out.println(code); code = String.format(""" public void print(%s o) { System.out.println(Objects.toString(o)); } """, type); System.out.println(code); // 另一种选择是引入新的实例方法String::formatted,该方法可以按如下方式使用 String source = """ public void print(%s object) { System.out.println(Objects.toString(object)); } """.formatted(type); System.out.println(source); // 附加方法 // 将添加以下方法来支持文本块; // //String::stripIndent():用于从文本块内容中去除附带的空白 //String::translateEscapes():用于翻译转义序列 //String::formatted(Object... args):简化文本块中的值替换 } }
《Java 13预览: 文本块》上有2条评论