五种琴弦
从Java 1.0开始,我们就有了字符串文字"like this"
。 我们还想要其他什么字符串? 其他编程语言为我们提供:
- 表达式插值:
s"I am ${age - 10} years old."
- 插值格式:
f"Price: $price%8.2f"
- 具有在编译时检查的内部语法的字符串:
r"[0-9]+([.,][0-9]*)?
或xml"<a href='http://java.sun.com'>The Java home page</a>"
- 不转义反斜杠的原始字符串:
raw"\.*"
- 可以包含换行符的多行字符串:
""" +-----+ | Cay | +-----+ """
在这里,我使用的语法让人想起Scala进行演示。 其他语言则做出了不同的选择。 例如,JavaScript使用反引号进行插值。
我最喜欢Java中的哪些功能? 对我而言,这将是编译时语法检查。 现在,IDE可以对特定的字符串(例如,正则表达式)进行有根据的猜测,并在其格式错误时发出警告。 但是,如果是编译时错误,那就更好了。
当然,这是一个难题。 除了注释处理,没有其他机制可以在编译时添加可插入检查。 可以提供检查字符串内容的注释,实际上Checker Framework就是这样做的。 但是您注释变量,而不是字符串文字,因此不是同一回事。
如果有一种进行插值和格式化的标准方法,那也很好。 现在,我们有String.format
和MessageFormat.format
,它们都是有用的但不兼容。
相反,Java 12为我们提供了原始/多行字符串。 那也很好
原始字符串
考虑例如使用正则表达式搜索句点。 正则表达式为\.
因为您必须在正则表达式中转义一个句号。 因此在Java中,它是Pattern.compile("\\.")
。 为了匹配反斜杠,它是Pattern.compile("\\\\")
。 这会变得很混乱。
实际上,它是如此令人困惑,以至于JEP 326的作者弄错了它,或者也许具有微妙的幽默感。 作者的示例是Pattern.compile("\\\"")
以匹配"
。 当然,您不需要在正则表达式中进行转义,因此Pattern.compile("\"")
可以正常工作,这证实了所有转义都是一团糟。
解决方法很简单。 将字符串括在反引号`...`
。 反引号内的所有内容都无需逃脱: Pattern.compile(`\.`)
但是,如果字符串包含反引号怎么办?
在Scala和Kotlin中,您使用"""
分隔符,但这引出了问题。如果字符串包含"""
怎么办?
这是Java设计师提出我以前从未见过的一个聪明的主意。 您可以使用任意数量的反引号来开始一个原始字符串,然后使用相同数量的反引号来结束它。 例如,如果您知道您的字符串中没有五个连续的反引号,请执行以下操作:
String s = `````. . .
. . .
. . .
. . .`````; // Five golden backticks :-)
字符串中的所有内容均照原样进行。 如果它是一些HTML或SQL或您在其他地方开发的任何内容,则将其粘贴。
实际上,“按原样”是一个例外。 即使源文件使用Windows风格的\r\n
行尾,所有行尾都被标准化为\n
。
美中不足的苍蝇
Stephen Colebourne指出,两个反引号可能会与空字符串混淆。 如果你有类似的东西
s = ``;
t = ``;
那么就不会将s
和t
设置为空字符串,而是将s
设置为字符串";\nt = "
。
那里有个很好的谜题。
原始字符串不能以反引号开头或结尾。 例如,假设您要将以下Markdown片段放入Java字符串中:
<
pre>“`
警报(“ Hello,World!”)
</pre>
You obviously can't add backticks at the start, so the best thing you can do is add a space or newline before the <code>```</code>. And the same holds for the end. Java requires that the ending delimiters exactly match the start. (In contrast, in Scala, you can write <code>"""Hello, "World""""</code>, and the compiler figures out that one of the terminal quotation marks belongs to the string.)So, you can write:
<pre>String markdown = `````
警报(“ Hello,World!”)
“
“”`.strip();
strip
调用将在开头和结尾删除\n
。 或者,也可以将换行符留在原处,如果它们无关紧要。
( strip
方法是Java 11的新功能。它与trim
相似,但是它去除了开头和结尾的Unicode空白,而trim
删除了≤32的字符,这已经不一样了。这些天,您应该使用strip
,而不是trim
。)
IDE支持
激活JDK 12的实验功能时,IntelliJ 2018.3可以将带有反斜杠的字符串转换为原始字符串。(有关详细信息,请参阅此博客文章 。)
我尝试转换老式的多行字符串:
private static final String authorPublisherQuery = "SELECT Books.Price, Books.Title\n"+ " FROM Books, BooksAuthors, Authors, Publishers\n"+ " WHERE Authors.Author_Id = BooksAuthors.Author_Id AND BooksAuthors.ISBN = Books.ISBN\n"+ " AND Books.Publisher_Id = Publishers.Publisher_Id AND Authors.Name = ?\n"+ " AND Publishers.Name = ?\n";
那是行不通的,但是没有理由为什么将来不会。
压痕管理
我更喜欢在最左边的列上排列多行字符串。 例如,
public static void main(String[] args) {String myNameInABox = `
+-----+
| Cay |
+-----+`.strip(); System.out.print(myNameInABox);}
它使多行字符串从Java代码中脱颖而出。 它为您放入原始字符串中的所有内容提供了足够的水平空间。
但是,很多人似乎更喜欢将多行字符串的内容与Java代码对齐的样式:
...String myNameInABox = `+-----+| Cay |+-----+`.align();System.out.print(myNameInABox);
align
方法(在Java 12中定义)删除空格的公共前缀以及空白行的开头和结尾。
这种方法存在风险。 如果混合使用制表符和空格,则每个制表符都计为一个空格。 在您的IDE中,有些东西看起来与您对齐,但与align
方法不一致。 当然,您的IDE可能会警告您这种情况。 IntelliJ 2018.3当前不这样做。
未走的路
关于新功能的许多讨论都在“ Amber Spec”邮件列表中进行,您可以在http://mail.openjdk.java.net/pipermail/amber-spec-observers/上进行观察,因此您可以看到具有哪些替代品被考虑。
关于是否应该自动删除缩进进行了激烈的讨论。 可以预见,这最终没有被采纳。
原始字符串中的Unicode转义如何处理? \u0060
应该是反引号吗? 理智盛行,决定“原始意味着原始”。
是否应该将两个反引号定为非法,因为``
可能与空字符串混淆? 不可以,只有一个简单的规则,即“任何数量的反引号”。
在开始反引号之后换行怎么样? 关于是否应该将其剥离存在一些来回的意见。 我仍然感到遗憾的是,没有对此问题给予更多关注。 在换行符中添加换行符将解决两个问题:初始反引号和最左列的对齐。
我胆怯地问,为什么关闭分隔符不能“至少与打开分隔符一样多的反引号”(类似于Scala),以便原始字符串可以以反引号结尾。 不幸的是,我没有回应。
令人惊讶的是,像这样的概念上简单的功能有多少细节。 撇开小调,这是一个非常受欢迎的礼物,正好赶上假期。
翻译自: https://www.javacodegeeks.com/2018/12/five-golden-backticks.html