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

我已经读过this article from 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用于文本块的目的是什么?我知道在普通字符串上使用它很容易理解。

完整的代码是here

最佳答案

有一个附带空格的概念。


JEP 355: Text Blocks (Preview)

编译时处理

文本块是String类型的常量表达式,就像字符串文字一样。但是,与字符串文字不同,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()定义),都会对前导空白字符进行计数。即使空白,也将计算最后一行的前导空白字符。最小值是这些计数中的最小值。

对于每条非空白行,将删除最小的前导空白字符,并删除所有尾随的空白字符。空行将替换为空字符串。


对于两个String,最小缩进均为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版本的重新缩进算法。


JEP 355

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

Specification for JEP 355

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


行终止符被标准化为ASCII LF字符(...)
删除偶然的空格,就像在内容中的字符上执行String::stripIndent一样。
转义序列被解释为字符串文字。

10-08 18:15