2015/03/09

[Java][Android]CharSequenceの話

例えばAlertDialogクラスのメソッドsetTitleは引数を1つ持っています。与えるべきなのはダイアログのタイトル部分に表示する文字列ですから型はStringかと思いきやCharSequenceというインターフェイスです。
よく見るとAndroidのクラスでStringを引数にするものは皆無といっていいのですが、普段意識することはあまりないと思います。
StringはCharSequenceを実装していますから、CharSequenceで宣言されている引数にはなんの問題もなくStringを使うことができます。なので意識していない人もすくなくないのではないかと思っています。
実際、J2EEで同じ役割を果たしているawtクラスの引数はStringです。普通のコーディングをしている方々ならStringで受けてくれた方がすっきりするのではないでしょうか。
(ちなみにCharSequenceは導入バージョンはJava1.4です。1.0から存在しているawtに採用されていないのは順番的に仕方ないと言えば仕方ないのですが…)

さて、CharSequenceはこんなインターフェイスです。

---

abstract charcharAt(int index)
Returns the character at index.
abstract intlength()
Returns the number of characters in this sequence.
abstract CharSequencesubSequence(int start, int end)
Returns a CharSequence from the start index (inclusive) to the end index (exclusive) of this sequence.
abstract StringtoString()
Returns a string with the same characters in the same order as in this sequence.

上から文字を取り出す、長さを求める、部分文字列を切り出す、そして文字列化する、というStringの基礎が明示されているだけです。
確かに表示するだけの文字列として最小限の命令セットにはなっています。
インターフェイス化はオブジェクト指向の基礎ですが、それだけではまだ、わざわざインターフェイスにする理由としては弱いと思っていました。
とはいえ現実問題インターフェイス化にはそれ相応のコストがかかります。JavaのStringはJava黎明期からコストパフォーマンスに非常に注意して作られたクラスです。普通に考えればダイレクトに使った方が実行効率はよいはずなのです。

StringサブクラスはUIを作っていればあちこちで使いたくなります。たった4つ、いや、toString()はObjectが持っていますから最低限3つの実装ではありますが、いちいち実装するのは上品ではありません。
なんでStringにしてくれなかったんだろう。そうしたらStringを継承するだけで済んだのに…

と思ってやっと気がつきました。理由は多分Stringがfinalだからです。
手前みそながら、前述のLabeledItemを設計した時です。

以下のコードはコンパイルできません。Stringがfinal宣言されているからです。

class SomeString extends String{

引数をinterfaceのCharSequenceにしておくことによって、機能拡張したStringぽいオブジェクトを引数に渡すチャンスを残した、と考えるべきでしょう。
おかげで私はLabeledItemを実装することができました。
awtは…多分、やらかしちゃったんでしょうね。
私はawtでがりがりUIを作った経験がないのでわかりませんが、黎明期のJavaコーダーさんのご意見を伺ってみたいものです。

ところでJavaには、CharSequenceを追加する他にもう一つの選択肢があったと思っています。
それは引数をObjectにすることです。
ObjectはtoString()を実装しています。つまりすべてのオブジェクトは文字として表すことができるように設計されています。個人的にはJavaの大変優れたデザインだと思っています。
Objectにしなかった理由は-これは本当に推測の域をでないのですが-JavaにおいてtoString()はデバッグ用という意思があったからではないかと思っています。
私はtoString()というデザインが好きな関係上、コードの中にUI上に表示するものとしてtoString()をオーバーライドすることがよくあるのですが、このスタイルはJavaとしてはアウトなのかもしれません。

さて、さらっと流しましたが、実はもうひとつの疑問がありました。
なんでStringはfinalなんでしょうか?
その話はこの次に。