The point of CharSequence
is to provide a read-only view to a character sequence, and that's it. This interface does not provide any string manipulation or searching methods. Those are out of scope.
The Interface Segregation Principle suggests that clients of a type should not depend on methods they don't use. Therefore, an interface should declare only the minimal useful set. If a different use case needs different methods, there should be a different interface.
A client that only needs a character source likely does not need search methods.
It is of course possible to overdo this Principle and end up with a thousand little interfaces. That's not good either. So the CharSequence
interface doesn't just contain the minimal charAt()
and length()
methods, but also the deeply related convenience method subSequence()
. (A CharSequence can likely provide a view onto a subsequence without a string copy, which is why this should be an instance method). Specifying toString()
is OK because that method would be inherited anyway from Object
. The methods chars()
and codePoints()
adapt a CharSequence
to a Stream
interface. Because these are default methods, they do not impose additional requirements for classes implementing CharSequence
.
The CharSequence
type is useful when a method needs a generic character source without specifying a particular implementation (e.g. String vs. CharBuffer vs. StringBuilder). The String#join()
and String#contains()
methods are good examples of using CharSequence
s.
It is not necessary for CharSequence
to provide a contains()
method because it can be implemented externally. While Java does not have the convenience of C#'s extension methods, a static method is essentially the same thing. So instead of boolean Editable#contains(CharSequence needle)
you would have a static boolean contains(CharSequence haystack, CharSequence needle)
. String search algorithms are a well-studied computer science topic. Different algorithms with different tradeoffs are readily available.
Further reading: