2

我刚刚尝试了 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我知道在普通字符串上使用它很容易理解。

完整的代码在这里

4

3 回答 3

6

有一个偶然空白的概念。

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. 转义序列被解释为字符串文字。

于 2019-09-20T15:36:30.333 回答
3

TLDR。您的示例字符串不相等,Java 告诉您它们不相等是正确的。

考虑阅读该String.stripIndent方法的描述。这是来自 jaxenter.com 帖子的释义:

stripIndent 方法删除所有行共有的多行字符串前面的空格,即将整个文本向左移动而不更改格式。

请注意“所有行都有共同点”这句话。

现在,将“that all lines have common”应用于以下文字字符串:

String hello2 = """
    Hello,
    First, notice that the final line of this example has zero spaces.
    Next, notice that all other lines of this example have non-zero spaces.
"""; // <--- This is a line in the text block.

关键是“0!= 3”。

于 2019-09-20T15:39:37.453 回答
2

测试jshell

String hello = """
    Hello,
    Java 13
    """;
hello.replace(" ", ".");

结果是

"Hello\nJava13\n"

注意:根本没有空格

String hello2 = """
    Hello,
    Java 13
""";
hello2.replace(" ", ".");

结果是

"....Hello\n....Java13\n"

请注意,两个结果在最后一行的最后一行中都没有空格\n,因此stripIndent()不会去除任何空格


stripIndent()与编译器对文本块的作用相同。例子

String hello3 = ""
    + "    Hello\n"
    + "    Java13\n"
    + "  ";
hello3.stripIndent().replace(" ", ".");

结果是

"..Hello\n..Java13\n"

也就是说,从所有 3 行中删除了两个空格;两个空格,因为最后一行有 2 个空格(其他行有更多,所以最多可以从所有行中删除 2 个空格)

于 2019-09-20T15:23:00.353 回答