2015/02/22

[Java]extendsできるStringを作る

Stringがfinalで宣言されていて困ったので、自前で作ってみました。

最初に断っておきますが、Stringがfinalになっているのには相応の意味がありますので、理解してから、適切な使い方をしてください。

AndroidのAlertDialogで選択するアイテムを上品に作ろうとしていました。
setSingleChoiceItemsの第一引数は一覧に表示する文字列の配列です。普通はここにStringの配列を渡しますね。
私はここに選択するべきデータを与えたいと思いました。ラベルとデータで2つの配列を持つのは恰好わるいじゃないですか。

データのtoStringを目的に合わせて実装しておけばいいよね、と思っていたのですが、そう簡単にはいきませんでした。
データがCharSeqece(なんでStringじゃないの?の謎はまたあとで)を継承していないので適用できませんとのこと。toStringを持ってるんだからObjectでいいじゃん、とも思ったのですがこれもまた掘り下げると長くなりそうなのであとで。

それならStringを継承すればいいよね、と思ったら今度はStringがfinal宣言されているのでextendsできませんということ。これもまた理由があるのですが、長くなるのであとで。

素直にインターフェイスCharSequenceを実装すればいいんでしょうが、CharSequenceは複数のメソッドを持っていて実装はそこそこ面倒です。私の場合、同じ目的でいくつかのメニュにこの仕掛けを適用しようとしていましたので、データ毎にCharSequence実装するのは上品ではないなぁと思わざるを得ませんでした。

そこで、発想を転換して作ったのがこのAbstractCharSequenceです。

public class AbstractCharSequence implements CharSequence{
 @Override
 public char charAt(int index) {
  return toString().charAt(index);
 }

 @Override
 public int length() {
  return toString().length();
 }

 @Override
 public CharSequence subSequence(int start, int end) {
  return toString().subSequence(start, end);
 }
}

AbstractCharSequenceはGoFデザインパターンでいうところの典型的なAdapterです。
AbstractCharSequenceはtoString()で返される文字列を元に、CharSequenceを実装するという実に本末転倒なクラスです
表示に使う文字列はtoString()をオーバーライドして与えます。
toStringはデバッグ用、というのがJavaの基本概念のような気もしてます。このやり方は邪道かもしれません。参考まで。

public class AValue : extends AbstractCharSequence{

 private String label;
 private String value;

 public AValue(String label, String value){
  this.label = label;
  this.value = value;
 }

 @Override
 public String toString(){
  return label;
 }

 public String getValue(){
  return value;
 }
}

AbstractCharSequenceを実装したクラスをsetSingleChoiceItemsに与えることによって、データそのものを表示するインスタンスにできます。

最初にも書きましたが、この手法はわりとJavaという言語の設計思想の根幹に踏み込んだことをしています。今回のような明確な目的を持たずに乱用するとよくないことがおこるかもしれません。
少なくとも、AbstractCharSequenceは実体を持ったクラスなので、真にObjectmもしくはStringから継承されるべきクラスにのみ適用すべきです。ほかにも継承する候補があるクラスはそちらを優先すべきです。対応は簡単で、CharSequenceを継承、実装してください。

0 件のコメント:

コメントを投稿