何时在API中使用CharSequence

26

我正在为一个包设计公共接口(API),我想知道,在通常情况下,是否应该使用CharSequence代替String(我主要是在谈论公共接口)。

这样做有什么缺点吗? 这被认为是一种良好的习惯吗?

如果用于类似标识符的目的(当值与哈希容器中的集合匹配时),怎么样呢?


1
这取决于API的预期功能。通常人们希望对“String”进行操作,但他们可能希望能够传递“StringBuilder”,因此更宽松一些是好的。但是,如果您需要在实现中将值复制到字符串中,则可能已经创建了一个设计上很慢的API。 - Mark Peters
更多讨论请参见问题Java中的CharSequence VS String?及其重复问题Java中CharSequence和String的确切区别是什么?。还有我的类图 - Basil Bourque
@BasilBourque 我认为这个问题有更高质量的答案。 - vbence
5个回答

35

CharSequence在通用库中很少使用。它应该通常用于字符串处理(操作、解析等)。

一般来说,您可以使用CharSequence执行与String相同的任何操作(因为您可以将每个CharSequence转换为String)。但是有一个重要的区别:CharSequence不能保证不可变!每当您处理一个String并在两个不同的时间点检查它时,您可以确定它每次都会具有相同的值。

但是对于一个CharSequence来说,这并不一定是真的。例如,有人可能会将StringBuilder传递到您的方法中,并在您处理它时进行修改,这可能会破坏许多合理的代码。

考虑以下伪代码:

public Object frobnicate(CharSequence something) {
  Object o = getFromCache(something);
  if (o == null) {
    o = computeValue(something);
    putIntoCache(o, something);
  }
  return o;
}

这看起来足够无害,如果你在这里使用了String,它将大多数工作(除了值可能被计算两次)。但是,如果something是一个CharSequence,那么它的内容在getFromCache调用和computeValue调用之间可能会发生更改。或者更糟糕的是:在computeValue调用和putIntoCache调用之间发生更改!因此:只有在有很大优势并且您知道缺点时才接受CharSequence。如果您接受CharSequence,则应记录API如何处理可变的CharSequence对象。例如:“在方法执行时修改参数会导致未定义的行为。”

3
例如,有人可以将一个 StringBuilder 传递到您的方法中,并在您使用它时进行修改。这不是许多类都能做到的吗,尤其是 List?难道我的库代码只能接受 ImmutableList 吗?是的,有人可能会在您的方法正在处理该列表时更改它,但他们会很愚蠢。如果这会危及您的不变量,请在验证输入之前进行防御性复制。 - Mark Peters
8
@MarkPeters:是的,这通常是正确的。在这里的重大区别是,对于“String”,每个Java开发人员的假设是它永远不会改变,这个假设已经被编码进了他们的内心(而大多数人则认为“List”是可变的)。因此,在从“String”切换到“CharSequence”时,他们可能会忽视一个“CharSequence”并不一定具有与“String”相同的良好属性的事实。 - Joachim Sauer
6
相较于String来说,CharSequence的另一个主要用途是处理“大型”字符序列,因为CharSequence的实现可能需要处理的数据并非同时全部存储在内存中。 - jtahlborn
@jtahlborn:没错,但我认为这是“字符串操作”的一个子集。 - Joachim Sauer
2
@jtahlborn:更准确地说,我指的是“字符串处理”,例如如果您要输出或附加到某个日志文件,则接受CharSequence可能很有用(例如能够记录StringBuilder)。解析器是另一个应该接受CharSequence的示例:它不需要将所有内容存储在内存中,只需要能够迭代每个字符。 - Joachim Sauer
显示剩余2条评论

7
这取决于您的需求,但我想指出 String 的两个优点。
CharSequence 文档 中可以看到:
每个对象可能由不同的类实现,并且不能保证每个类都能测试其实例与其他实例的相等性。因此,使用任意 CharSequence 实例作为集合中的元素或映射中的键是不合适的。
因此,每当您需要一个可靠的 equals/hashCode 或者 Map 时,就需要将实例复制到 String(或其他)中。
此外,我认为 CharSequence 没有明确提到实现必须是不可变的。您可能需要进行防御性拷贝,这可能会减慢您的实现速度。

给定的链接显示页面未找到! - Visruth
@VisruthCV 更新了链接,现在链接指向 Java 7 版本。 - Matthias Meid

5

Java CharSequence是一个接口。根据API的说法,CharSequence已经在CharBufferSegmentStringStringBufferStringBuilder类中实现。因此,如果您想从所有这些类中访问或接受您的API,则选择CharSequence。如果不需要,则String非常适合公共API,因为它非常简单且人人都知道。请记住,CharSequence只给您4种方法,因此,如果通过方法接受CharSequence对象,则输入操作能力将受到限制。


1
我不同意你的结论。CharSequence 中的一个方法是 toString(),因此任何可以使用 String 的操作都可以在任意 CharSequence 上执行(只需调用它的 toString() 方法,并使用您想要的任何操作能力)。 - Andrzej Doyle
6
toString() 的实现可能非常昂贵。大多数实现需要将整个字符序列复制到新数组中。如果你的第一步是从 CharSequence 获取一个 String,那么你为隐藏性能损失提供了灵活性。这并没有太多价值,最好使用一个 String 并让用户进行转换,这样他们就非常清楚这种惩罚的代价。 - Mark Peters
@AndrzejDoyle:Mark Peters已经为我回答了,我想再补充一点。您建议使用CharSequence.toString()方法进一步操作输入,那么为什么不直接将输入作为String接受呢?这样您就不必将CharSequence转换为String了。 - SajithA
所以,如果你想从所有这些类中访问或接受你的API,那么CharSequence就是你的选择。我认为那个句子有点倒过来了;你的API应该接受CharSequence,而不是相反。 "...然后对于公共API,String非常好,因为它非常容易。" 真的吗?我认为可以从String API中学到很多东西(即使仅仅是正则表达式)。最后一点:CharSequence 的这两种方法可能会比 String 给你带来优势:subSequencecharAt - Maarten Bodewes

4
如果一个参数在概念上是一个字符序列,那么请使用CharSequence。
String技术上是一个字符序列,但我们通常不会这样考虑;String更像是一个原子/整体,我们通常不关心单个字符。
想想int - 虽然int技术上是一系列位,但我们通常不关心单个位。我们将int作为原子事物来操作。
因此,如果你要对参数进行的主要工作是遍历其字符,请使用CharSequence。如果您要将参数作为一个原子事物来操作,请使用String。

0

你可以使用CharSequence来保存密码,因为不推荐使用String进行该目的。实现应该有一个dispose方法来擦除明文数据。


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