为什么 Java 中没有 String.Empty?

318

我知道每次输入空字符串字面量""时,都会引用字符串池中相同的String对象。

但是为什么String API没有包括public static final String Empty = "";,这样我就可以使用对String.Empty的引用了呢?

它至少能节省编译时间,因为编译器将知道引用现有的String对象,而不必检查是否已经被创建以供重用,对吧?而且,在许多情况下,我认为大量的字符串字面量,特别是微小的字面量,是一种"代码异味"。

所以,没有String.Empty是出于伟大的设计原因,还是语言创建者只是不分享我的看法呢?


6
Aidanc: 我认为他的意思是在一些情况下,你会写像 outputBlah = "" 这样的代码,他可能更喜欢用 something == String.Empty 来代替 something.Length > 0(这样你就省略了对空值的检查)。 - Skurmedel
5
他正在寻找像 Collections.EMPTY_SET 这样的“空成员”,而不是用于检查字符串“是否为空”的函数。 - Tim Stone
3
有一个 String.isEmpty() 函数…你为什么需要 String.EMPTY - Buhake Sindi
14
String.isEmpty()不会返回一个空字符串。 - Steve Kuo
5
在实际编码中,它可能没有任何优势,但从可读性的角度来看,它具有价值。(作为一个从C#转向Java的开发人员,我很想念这个特性)。正如其他人所说,使用 "" 会产生潜在错误,因为无法确定空字符串是有意设置的还是代码尚未完成。另外,在阅读代码时容易忽略 """ "'' 之间的区别。我认为这样做有价值,因为可以避免在代码中使用字符串字面量,并明确声明将变量设置为空。 - SouthShoreAK
显示剩余7条评论
12个回答

215

String.EMPTY 是12个字符长,而 "" 只有两个字符长,在运行时它们都引用着完全相同的内存实例。我并不确定为什么 String.EMPTY 会节省编译时间,事实上,我认为后者更可能节省时间。

尤其是考虑到 String 是不可变的,你不能先拿到一个空字符串,然后对它进行一些操作 - 最好使用 StringBuilder (或者如果你想要线程安全的话可以使用 StringBuffer),然后将其转换为一个字符串。

更新
从您对该问题的评论中:

  

这是 TextBox.setText(""); 驱使我做出这个决定的。

我认为在你合适的类中提供一个常量是完全合法的:

private static final String EMPTY_STRING = "";

然后在你的代码中像这样引用它

TextBox.setText(EMPTY_STRING);

这样至少可以明确表达你想要一个空字符串,而不是在IDE中忘记填写字符串或类似情况。


21
我仍会点赞你,但你提到 StringBuilder 却没有谈到在十有九次情况下使用 StringBuilder 而不是连接操作是完全不适当的,让我感到有些不爽。 - Randolpho
110
我倾向于使用string.empty,因为它更加明确。另外,在某些情况下,很难从视觉上区分""和像"'"这样的东西。最终,正如其他人指出的那样,这只是一种毫无意义的风格问题,在我们对真正的工作感到无聊时,会拿来争论。=) - JohnFx
1
-1 - String.EMPTY 必须是一个 final static 引用,这样在运行时它将只是一个唯一的引用。如果你使用 "",每次使用它都会复制一个引用。我认为这就是原始问题的背景... - caligari
1
我和@hfrmobile一样 - 我没有测试过这个,但是关于这个更有效率是因为字符数量的问题,我不同意。String.Empty 是一个静态变量,被所有实例共享,而 "" 每次都会实例化一个新的空字符串。String.Empty 更有效率。 - Aaron Newton
1
不要忘记对于 "",我们必须添加 //$NON-NLS-1$,这是 13 个字符 + 2 个 ""。因此,String.EMPTY 在长度和美观度上都更胜一筹。 - Yaza
显示剩余24条评论

204
使用 org.apache.commons.lang.StringUtils.EMPTY

51
这看起来比一个空的""好看得多,也更容易阅读。我希望这不仅仅是我一个人这样认为。 - Lakatos Gyula
4
@LakatosGyula - 我认为这可能只是你个人的观点。熟练的Java程序员在阅读""时不会有任何问题,而且大多数人可能会强烈反对除特定情况外使用EMPTY,在这种情况下,EMPTY具有特定领域的含义。(而在这种情况下,可能有更合适的名称。) - Stephen C
18
不仅是你。我曾经从Java转到.NET开发,并且String.Empty是一个我非常高兴在框架中发现的功能。相较于一对空引号,我喜欢它更显式的特性。 - yohohoho
80
@StephenC 当我看到一个空的 "" 时,我脑海中首先跳出的是这是一个错误,有人没有完成函数等。使用 String.EMPTY,我确切地知道开发人员打算返回一个空字符串。 - Lakatos Gyula
6
还有一个场景是当代码检查工具(linter)提示我们"使用常量代替某某某"时,这也很有帮助。每个程序员都知道“某某某”并没有魔法,但不必向客户解释它的含义会更好。 - LizH
显示剩余3条评论

33

如果你想比较一个空字符串,而不用担心null值,可以按照以下方式操作。

if ("".equals(text))

最终你应该做你认为最清晰的事情。大多数程序员认为 "" 表示空字符串,而不是某个人忘记放入任何内容的字符串。

如果你认为有性能优势,你应该进行测试。如果你认为不值得自己测试,那么这表明它真的不值得。

听起来你试图解决一个在语言设计15年前就已经解决的问题。


1
我可能有点晚了,但由于Java字符串是不可变的,因此我认为JVM中的所有空字符串只是对同一个字符串对象的不同引用。因此,以下内容也是正确的:if ("" == text) - Ajoy Bhatia
13
问题在于你可以创建新的空字符串。if ("" == new String()) 是错误的。更好的测试是 if(text.isEmpty()) - Peter Lawrey
1
@AjoyBhatia - 只有字符串被联接,才能这样做。https://dev59.com/82kv5IYBdhLWcg3wdQnm - Davor

14

不要仅仅说“字符串内存池以字面量形式重复使用,问题已解决”。编译器在幕后的操作并不是这里的重点。这个问题是合理的,特别是考虑到它收到的赞数。

这涉及到对称性,没有对称性,API对人类来说就更难使用。早期的Java SDK明显忽略了这个规则,现在有些晚了。以下是我能想到的一些例子,欢迎贡献您所钟爱的例子:

  • BigDecimal.ZERO,但没有AbstractCollection.EMPTY、String.EMPTY
  • Array.length但List.size()
  • List.add(),Set.add(),但Map.put(),ByteBuffer.put(),还有StringBuilder.append()、Stack.push()等等

即使您将List参数命名为length(),由于它是一个方法,仍然需要括号。Array.length是一个公共的final变量,这只能工作因为Arrays是不可变的。所以你仍然有Array.length和List.length()。我认为这更加混乱和容易出错。至于.append()和.push(),虽然它们执行类似的任务,但我认为它们的命名是合适的。追加字符串就是您正在做的事情,但您不会“追加”一个Stack,而是推入和弹出值。StringBuilder.push()将意味着StringBuilder.pop(),这是不可能的。 - Craig Parton
来自模板/泛型,一致的接口也有助于算法。如果算法需要集合的长度,我们只关心length(T)或T.length()。同样,向栈、列表或字符串的末尾添加内容可以通过通用的add()或append()完成。你提到Java中的数组是不可变/内置类型,具有暴露的length属性。那没问题,这并不意味着编译器不能处理或生成length(T)或T.length()的代码。Kotlin为各种情况生成了许多内置方法。 - Slawomir
但是一个一致命名的length()方法只能让我们检查长度。那有多有用呢?如果你的目标是通过接口抽象列表和数组,使它们可用于某种方式,你还需要一种一致的读写数据的方法。所以现在你需要生成get()、set()和add()方法。本质上,你正在创建一个不太功能性的数组列表视图。由于Arrays.asList()可用、易于使用且轻量级,为什么要重新发明轮子呢?数组、列表、字符串构建器和堆栈都有特定的用途。设计你的接口以使用最佳匹配似乎更好。 - Craig Parton

9

Apache StringUtils也解决了这个问题。

其他选项的缺点:

  • isEmpty() - 不具备空值安全性。如果字符串为空,会抛出NPE异常。
  • length() == 0 - 同样不具备空值安全性。也无法考虑到空格字符串。
  • 与EMPTY常量进行比较 - 可能不具备空值安全性。存在空格问题。

尽管需要使用另一个库StringUtils,但它非常有效,可以节省大量时间和麻烦,检查空值或优雅地处理NPE异常。


3
那么...似乎唯一安全的选择是可怕的Yoda条件:"".equals(s) - Lie Ryan

9

看起来这是显而易见的答案:

String empty = org.apache.commons.lang.StringUtils.EMPTY;

令人惊喜的是,“空初始化”代码不再使用“魔法字符串”,而是使用常量。


8
如果你真的想要一个String.EMPTY常量,你可以在你的项目中创建一个名为“Constants”的实用静态final类(例如)。这个类将维护你的常量,包括空字符串...
同样的思路,你可以创建ZERO、ONE int常量... 这些常量在Integer类中不存在,但是像我评论的那样,写起来和阅读起来都很麻烦:
for(int i=Constants.ZERO; ...) {
    if(myArray.length > Constants.ONE) {
        System.out.println("More than one element");
    }
}

等等。


这通常被认为是不好的做法。https://en.wikipedia.org/wiki/Constant_interface - neun24

5
所有这些""字面量都是同一个对象。为什么要增加所有这些额外的复杂性呢?这只会使得代码更长,更不清晰(编译器的成本也很小)。由于Java的字符串是不可变对象,除了可能作为效率问题之外,根本没有必要区分它们,但对于空字符串字面量来说,这并不是什么大问题。
如果你真的想要一个EmptyString常量,那就自己创建一个吧。但它只会鼓励更冗长的代码;永远不会有任何好处。

33
“x = String.Empty”比“x = ”更能表达意图。后者可能是一个意外的省略。说绝对没有任何好处是不正确的。 - Jeffrey L Whitledge
@Jeffrey:我不认为我特别同意。我想这是其中一件没有硬性规定的事情。 - Donal Fellows
是的,需要指出的是Java编译器在创建字符串池中的新实例之前会检查字符串字面量是否已经存在。 - rds
1
@Jeffrey - 知道这是一个非常古老和主观的讨论。x = String.Empty 传达了意图,没错。但是假设语言提供一个常量 String.Empty,当你遇到 x = "" 时,你仍然和如果没有这样一个常量一样清楚地知道其意图。你需要保证世界上所有 Java 代码中打算使用空字符串的地方都不使用 "",才能获得你提到的信息增益。具有讽刺意味的是,C# 确实使用了一个常量并鼓励其使用,所以正如我所说,我知道这是一个非常见解的讨论。 - chiccodoro
@chiccodoro - 是的,没错。这就是为什么空字符串字面量 "" 应该是非法的,以避免意外发生。开玩笑的! - Jeffrey L Whitledge

4
补充Noel M所说的,你可以看看这个问题,这个答案显示常数被重复使用。

http://forums.java.net/jive/message.jspa?messageID=17122

String constant are always "interned" so there is not really a need for such constant.

String s=""; String t=""; boolean b=s==t; // true

1
链接已失效。 - dieter

3
晚了一点回答,但我认为它对这个主题增加了新的内容。之前的回答都没有回答原始问题,有些尝试证明常量的缺乏,而其他人则展示了我们如何处理缺乏常量的方式。但是没有人提供一个令人信服的理由来说明常量的好处,因此其缺乏仍然没有得到适当的解释。常量将是有用的,因为它可以防止某些代码错误不被注意到。假设您有一个包含数百个引用"" 的大型代码库。某人在浏览代码时修改其中一个并将其更改为" "。这样的更改很可能会在生产中不被注意到,此时它可能会导致一些难以检测的问题。相比之下,如果一个名为EMPTY的库常量遇到同样的错误,就会为类似EM PTY的东西生成编译器错误。自己定义常量仍然更好。某人仍可能错误地更改其初始化,但由于其广泛的使用,这种错误的影响要比单个用例中的错误更难被忽视。这是使用常量而不是文字值时获得的一般好处之一。人们通常认为,在几十个地方使用常量作为值允许您轻松地在一个地方更新该值。但很少有人承认这也可以防止该值被意外修改,因为这样的更改会在任何地方显示出来。所以,是的,""比EMPTY短,但EMPTY比""更安全使用。因此,回到最初的问题,我们只能推测语言设计者可能没有意识到提供常量用于频繁使用的文字值的好处。希望我们有一天会看到Java中添加字符串常量。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接