Why CharSequence Doesn’t Define contains(CharSequence) in Java

androiddesigninterfacesjava

This applies to to both Java SE & Android, as the contracts are identical.

CharSequence does not define a contains(CharSequence) method. I can't seem to find a reason why, and including it would be quite useful, preventing the need to call CharSequence#toString() to check for a sequence of characters.

For example, in Android, users are forced to call Editable#toString() to see if it contains a sequence of characters, albeit Editable implements CharSequence, which can be avoided if CharSequence defined contains(CharSequence).

What's the idea behind this design choice? Is it a potential oversight, or is there a design reason for this?

Best Answer

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 CharSequences.

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:

Related Topic