【Android】Adapterをforeachで使えるようにする

Adapterってあるじゃないですか。ListViewとか、ListViewとか、ListViewで使うあれです。あれ、foreachで回したくなりません?

いや確かに、Adapterを作る以上、ほとんどの場合は何らかのIterableなもの(例外はCursorぐらい?あれもiteratorパターンではあるけど)をコンストラクタで渡すのであって、それをメンバ変数なりなんなりで保持しておき、foreachしたい時はそれを使えばいいでしょう。

ただ、ArrayAdapter<T>がうっかりaddを実装しており、adapterとiterableなメンバの同期を取るのってめんどくさくない?adapterに全部あるんだからそっから全部とればいいじゃん、とか、そう言うことを度々思うので、取れるようにします。

Iterable<T>とIterator<E>を実装する

今更感があるんですが、foreach構文(Javaは拡張for文って呼ぶんだっけ)を適用するためにはIteratable<T>を実装する必要があります。このインターフェースで定められているメソッドiteratorメソッドのみです。で、これは何をする必要があるのかと言うと、なんでもいいからIterator<E>を返してやるだけです。

Iterator<E>のメソッドhasNextnextの二つが必須になっています。removeは任意のオペレーションとのことなので、まぁ実装したければどうぞって感じです。

つまり、「次の要素があるか?」と「次の要素は何か?」の二つがわかればIteratorパターンは実装できるわけですね。これをAdapterの観点から考えてみましょう。

AdapterのメソッドからIteratorパターンを作る

Adapterの親子関係は若干複雑で、直接Adapterを実装しているのはListAdapterSpinnerAdapterだけです。

このAdapterを実装した二つのインターフェースを両方実装しているのがBaseAdapterという抽象クラスになっています。ArrayAdapter<T>などの具象クラスは、このBaseAdapterを継承しています。

だからリファレンスではこういう風に表現されているわけです。

java.lang.Object
   ?android.widget.BaseAdapter
    ?android.widget.ArrayAdapter<T>

とは言え、辿っていけばちゃんとすべての具象クラスがAdapterインターフェースを実装していることになります。そしてAdapterにはItartorパターンを作る上で必要そうなgetCountメソッドgetItemメソッドが規定されています。

コード

早速作ってみましょう。

[2014/10/15修正]なぜかremoveが実装されていなかったので追加しておきました。

public final class AdapertToIterable<T> implements Iterable<T>, Iterator<T> {
    
    private int _position;
    private int _maxCount;
    private Adapter _adapter;
    
    public AdapertToIterable(Adapter adapter) {
        _adapter = adapter;
        _maxCount = !adapter.isEmpty() ? adapter.getCount() : 0;
    }
    
    @Override
    public Iterator<T> iterator() {
        return this;
    }
    
    @Override
    public boolean hasNext() {
        return _position < _maxCount;
    }
    
    @Override
    public T next() {
        return (T) _adapter.getItem(_position++);
    }
    
    @Override
    public void remove() {
        throw new UnSupportedOperationException();
    }
    
}

超簡単ですね。これでIterableなAdapterを手に入れることが出来ました。

まとめ

特にまとめるほどのあれもないです。