在Android中编程,大多数文本值都在CharSequence中。

为什么呢?使用CharSequence而不是String的好处是什么,主要影响是什么?

在使用它们和从一种转换到另一种时,主要的区别是什么,预期会出现什么问题?


当前回答

CharSequence =接口字符串=具体实现

CharSequence是一个接口。 有几个类实现了这个接口。 String就是这样一个类,是CharSequence的具体实现。

你说:

从一个转换到另一个

没有从字符串进行转换。

每个String对象都是一个CharSequence。 每个CharSequence都可以生成一个String。叫CharSequence进行::toString。如果CharSequence恰好是String,则该方法返回对自己对象的引用。

换句话说,每个字符串都是CharSequence,但并不是每个CharSequence都是String。

编程到接口

在Android中编程,大多数文本值都在CharSequence中。 为什么呢?使用CharSequence而不是String的好处是什么,主要影响是什么?

通常,面向接口编程比面向具体类编程更好。这产生了灵活性,因此我们可以在特定接口的具体实现之间切换,而不会破坏其他代码。

在开发供各种程序员在各种情况下使用的API时,编写代码时尽可能提供和采用最通用的接口。这使得调用程序可以自由地使用该接口的各种实现,无论哪种实现最适合其特定上下文。

例如,看看Java集合框架。如果你的API给出或接受一个有序的对象集合,将你的方法声明为使用List,而不是ArrayList, LinkedList或任何其他第三方的List实现。

当编写一个快速而肮脏的小方法只供您的代码在一个特定的地方使用时,而不是编写一个API用于多个地方时,您不需要使用更通用的接口而不是特定的具体类。但即便如此,使用最通用的界面也会造成伤害。

在使用它们时,主要的区别是什么,预期会出现什么问题,

对于String,你知道你有一个单独的文本,完全在内存中,并且是不可变的。 使用CharSequence,您不知道具体实现的特定特性可能是什么。

CharSequence对象可能表示一个巨大的文本块,因此有内存影响。或者可能有许多单独跟踪的文本块,在调用toString时需要将它们拼接在一起,因此存在性能问题。实现甚至可能从远程服务检索文本,因此存在延迟问题。

从一个转换到另一个?

你通常不会来回转换。字符串是一个CharSequence。如果你的方法声明它接受一个CharSequence,调用它的程序员可能会传递一个String对象,或者传递其他东西,比如StringBuffer或StringBuilder。您的方法代码将简单地使用所传递的任何东西,调用任何CharSequence方法。

The closest you would get to converting is if your code receives a CharSequence and you know you need a String. Perhaps your are interfacing with old code written to String class rather than written to the CharSequence interface. Or perhaps your code will work intensively with the text, such as looping repeatedly or otherwise analyzing. In that case, you want to take any possible performance hit only once, so you call toString up front. Then proceed with your work using what you know to be a single piece of text entirely in memory.

扭曲的历史

注意对已接受的答案所作的评论。CharSequence接口被改造到现有的类结构上,因此有一些重要的微妙之处(equals() & hashCode())。请注意标记在类/接口上的各种Java版本(1、2、4和5)——这些年来变化很大。理想情况下CharSequence应该从一开始就存在,但这就是生活。

下面的类图可以帮助您了解Java 7/8中字符串类型的总体情况。我不确定是否所有这些都存在于Android中,但总体上下文可能仍然对你有用。

其他回答

字符串是CharSequences,所以你可以只使用字符串,不用担心。Android只是试图通过允许你指定其他CharSequence对象来提供帮助,比如stringbuffer。

这几乎肯定是性能方面的原因。例如,假设一个解析器通过一个包含字符串的500k ByteBuffer。

有3种方法返回字符串内容:

Build a String[] at parse time, one character at a time. This will take a noticeable amount of time. We can use == instead of .equals to compare cached references. Build an int[] with offsets at parse time, then dynamically build String when a get() happens. Each String will be a new object, so no caching returned values and using == Build a CharSequence[] at parse time. Since no new data is stored (other than offsets into the byte buffer), the parsing is much lower that #1. At get time, we don't need to build a String, so get performance is equal to #1 (much better than #2), as we're only returning a reference to an existing object.

除了使用CharSequence获得的处理收益之外,还可以通过不复制数据来减少内存占用。例如,如果您有一个包含3段文本的缓冲区,并且希望返回全部3段或单个段落,您需要4个字符串来表示。使用CharSequence,您只需要1个数据缓冲区,以及4个CharSequence实现实例来跟踪开始和长度。

CharSequence进行

CharSequence是一个接口,而不是实际的类。接口只是类实现接口时必须包含的一组规则(方法)。在Android中,CharSequence是各种类型文本字符串的保护伞。以下是一些常见的错误:

字符串(没有样式跨度的不可变文本) StringBuilder(没有样式跨度的可变文本) SpannableString(具有样式跨度的不可变文本) SpannableStringBuilder(具有样式跨度的可变文本)

(你可以在这里阅读更多关于它们之间的区别。)

如果你有一个CharSequence对象,那么它实际上是一个实现CharSequence的类的对象。例如:

CharSequence myString = "hello";
CharSequence mySpannableStringBuilder = new SpannableStringBuilder();

使用CharSequence这样的通用伞形类型的好处是,您可以用一个方法处理多个类型。例如,如果我有一个方法,以CharSequence作为参数,我可以传入String或SpannableStringBuilder,它将处理任何一个。

public int getLength(CharSequence text) {
    return text.length();
}

字符串

你可以说String只是CharSequence的一种。然而,与CharSequence不同的是,它是一个实际的类,因此您可以从它创建对象。所以你可以这样做:

String myString = new String();

但你不能这样做:

CharSequence myCharSequence = new CharSequence(); // error: 'CharSequence is abstract; cannot be instantiated

因为CharSequence只是String遵循的规则列表,你可以这样做:

CharSequence myString = new String();

这意味着任何时候一个方法请求CharSequence,都可以给它一个String。

String myString = "hello";
getLength(myString); // OK

// ...

public int getLength(CharSequence text) {
    return text.length();
}

然而,反过来就不对了。如果这个方法接受一个String参数,你不能传递给它一个通常被认为是CharSequence的东西,因为它实际上可能是一个SpannableString或其他类型的CharSequence。

CharSequence myString = "hello";
getLength(myString); // error

// ...

public int getLength(String text) {
    return text.length();
}

在实际的Android代码中出现的一个问题是将它们与CharSequence进行比较。Equals是有效的,但不一定像预期的那样工作。

EditText t = (EditText )getView(R.id.myEditText); // Contains "OK"
Boolean isFalse = t.getText().equals("OK"); // will always return false.

应由

("OK").contentEquals(t.GetText()); 

我认为最好使用CharSequence。原因是String实现了CharSequence,因此你可以传递一个String到一个CharSequence,然而你不能传递一个CharSequence到一个String,因为CharSequence不实现String。此外,在Android中,EditText.getText()方法返回一个可编辑的,它也实现了CharSequence,可以很容易地传递到一个,而不容易传递到字符串。CharSequence处理所有!