首页 > 解决方案 > 如何在文本块中处理意图(Java 13)

问题描述

我刚刚尝试了 Java 13 中的新文本块功能,遇到了一个小问题。

从 Jaxcenter 阅读了这篇文章

右三引号会影响格式。

String query = """
            select firstName,
            lastName,
            email
            from User
            where id= ?
        """;

System.out.println("SQL or JPL like query string :\n" + query);

上述格式效果很好。为了与结束分隔符 (""") 对齐,多行字符串在每行之前留有空格。

但是当我尝试比较以下两个文本块字符串时,它们在输出控制台中的格式相同,但它们不等于,即使在stripIntent.

String hello = """
    Hello,
    Java 13
    """;

String hello2 = """
    Hello,
    Java 13
""";

System.out.println("Hello1:\n" + hello);
System.out.println("Hello2:\n" + hello);

System.out.println("hello is equals hello2:" + hello.equals(hello2));

System.out.println("hello is equals hello2 after stripIndent():" + hello.stripIndent().equals(hello2.stripIndent()));

输出控制台如下:

hello is equals hello2:false
hello is equals hello2 after stripIndent():false

我不确定哪里错了,或者这是文本块的设计目的?

更新:只需打印 hello2 stripIntent,

System.out.println("hello2 after stripIntent():\n" + hello2.stripIndent());

每行之前的空格不会stripIntent按预期删除。

更新:阅读相关的java文档后,我认为在文本块编译后,它应该已经剥离了块中行的左意图。文本块的目的是什么?stripIntent我知道在普通字符串上使用它很容易理解。

完整的代码在这里

标签: javastringjlsjava-13

解决方案


有一个偶然空白的概念。

JEP 355:文本块(预览版)

编译时处理

文本块是字符串类型的常量表达式,就像字符串文字一样。但是,与字符串文字不同,文本块的内容由 Java 编译器分三个不同的步骤处理:

  • 内容中的行终止符被转换为 LF (\u000A)。这种翻译的目的是在跨平台移动 Java 源代码时遵循最小意外原则。

  • 为匹配 Java 源代码的缩进而引入的内容周围的附带空白被删除。

  • 内容中的转义序列被解释。将解释作为最后一步意味着开发人员可以编写转义序列,例如 \n,而不会被前面的步骤修改或删除。

...

附带的空白

这是使用点来可视化开发人员为缩进添加的空格的 HTML 示例:

String html = """
..............<html>
..............    <body>
..............        <p>Hello, world</p>
..............    </body>
..............</html>
..............""";

由于开始分隔符通常与使用文本块的语句或表达式出现在同一行,因此每行开始有 14 个可视化空格这一事实没有实际意义。在内容中包含这些空格意味着文本块表示的字符串与连接字符串文字表示的字符串不同。这会损害迁移,并且会反复出现令人惊讶的问题:开发人员极有可能不希望字符串中出现这些空格。此外,结束分隔符的位置通常与内容对齐,这进一步表明 14 个可视化空间是微不足道的。
...
因此,对文本块内容的适当解释是将每行开头和结尾的偶然空白与基本空白区分开来。Java 编译器通过删除附带的空白来处理内容,以产生开发人员想要的内容。

你的假设

    Hello,
    Java 13
<empty line>

等于

....Hello,
....Java 13
<empty line>

是不准确的,因为这些是必不可少的空格,编译器或String#stripIndent.

为了清楚起见,让我们继续将偶然的空白表示为一个点。

String hello = """
....Hello,
....Java 13
....""";

String hello2 = """
    Hello,
    Java 13
""";

让我们打印它们。

Hello,
Java 13
<empty line>

    Hello,
    Java 13
<empty line>

让我们调用String#stripIndent两者并打印结果。

Hello,
Java 13
<empty line>

    Hello,
    Java 13
<empty line>

要了解为什么什么都没有改变,我们需要查看文档。

String#stripIndent

返回一个字符串,其值为该字符串,并从每行的开头和结尾删除了附带的空格。

然后,最小压痕(min)如下确定。对于每个非空行(由 定义isBlank()),计算前导空白字符。即使是空白,最后一行的前导空白字符也会被计算在内。最小值是这些计数中的最小值。

对于每个非空白行,删除最少的前导空白字符,并删除任何尾随空白字符。空行替换为空字符串。

对于这两个Strings,最小缩进是0

Hello,          // 0
Java 13         // 0    min(0, 0, 0) = 0 
<empty line>    // 0

    Hello,      // 4
    Java 13     // 4    min(4, 4, 0) = 0
<empty line>    // 0

String#stripIndent使开发人员可以访问编译器使用的重新缩进算法的 Java 版本。

杰普 355

重新缩进算法将在 Java 语言规范中成为规范。String::stripIndent开发人员将可以通过一个新的实例方法来访问它。

JEP 355 规范

由文本块表示的字符串不是内容中字符的文字序列。相反,由文本块表示的字符串是按顺序对内容应用以下转换的结果:

  1. 行终止符被规范化为 ASCII LF 字符 (...)

  2. 附带的空白被删除,就像String::stripIndent对内容中的字符执行一样。

  3. 转义序列被解释为字符串文字。


推荐阅读