- PR -

java.util.Collection のメソッドがサブインターフェースの List にもあるのはなぜ?

投稿者投稿内容
unibon
ぬし
会議室デビュー日: 2002/08/22
投稿数: 1532
お住まい・勤務地: 美人谷        良回答(20pt)
投稿日時: 2003-11-28 09:53
unibon です。こんにちわ。

Java2 の Collections API を見てて、
メソッドや型階層に冗長さがあることが気になっています。

(1) Java2 の java.util パッケージにおいて、
Collection インターフェースはつぎのように 15 個のメソッドを持ちます。
コード:
public interface Collection {
    int size();
    boolean isEmpty();
    boolean contains(Object o);
    Iterator iterator();
    Object[] toArray();
    Object[] toArray(Object a[]);
    boolean add(Object o);
    boolean remove(Object o);
    boolean containsAll(Collection c);
    boolean addAll(Collection c);
    boolean removeAll(Collection c);
    boolean retainAll(Collection c);
    void clear();
    boolean equals(Object o);
    int hashCode();
}


このサブインターフェースである List は
つぎのように 25 個のメソッドを持ちます。
コード:
public interface List extends Collection {
    int size();
    boolean isEmpty();
    boolean contains(Object o);
    Iterator iterator();
    Object[] toArray();
    Object[] toArray(Object a[]);
    boolean add(Object o);
    boolean remove(Object o);
    boolean containsAll(Collection c);
    boolean addAll(Collection c);
    boolean addAll(int index, Collection c);
    boolean removeAll(Collection c);
    boolean retainAll(Collection c);
    void clear();
    boolean equals(Object o);
    int hashCode();
    Object get(int index);
    Object set(int index, Object element);
    void add(int index, Object element);
    Object remove(int index);
    int indexOf(Object o);
    int lastIndexOf(Object o);
    ListIterator listIterator();
    ListIterator listIterator(int index);
    List subList(int fromIndex, int toIndex);
}


その内の 15 個は Collection がすでに持っているものです。
というか Collection が持っている 15 個のメソッドすべてを
List が再度持ち直しています。
なぜこのように冗長にしているのでしょうか。
コードを書く上でこうすべきなのでしょうか。
それとも型階層をブラウズする際にスーパクラスを何段も遡らなくて済むように、
余計な配慮かなにかのためでしょうか。

(2) また、もうひとつの類似の疑問としては、
つぎのように、ArrayList は AbstractList のサブクラスですが、
List も implements しています。
コード:
public class ArrayList extends AbstractList
        implements List, RandomAccess, Cloneable, java.io.Serializable {
}


しかし、つぎのように AbstractList の段階ですでに List を implements しています。
コード:
public abstract class AbstractList extends AbstractCollection implements List {
}


したがって、ArrayList が List を implements する必要はなく、冗長だと思います。
上記(1)はメソッドの冗長さでしたが、こんどは型の冗長さになっています。
おそらくこれらのアーキテクチャになった背景の考えかたは(1)も(2)も同じなのでは
と推測しますが、この推測は合っているでしょうか。
ぽん
大ベテラン
会議室デビュー日: 2003/05/13
投稿数: 157
投稿日時: 2003-11-28 10:08
引用:

unibonさんの書き込み (2003-11-28 09:53) より:

その内の 15 個は Collection がすでに持っているものです。
というか Collection が持っている 15 個のメソッドすべてを
List が再度持ち直しています。
なぜこのように冗長にしているのでしょうか。


全くの推測ですが・・・
JavaDocを出力するためとか?
H2
ぬし
会議室デビュー日: 2001/09/06
投稿数: 586
お住まい・勤務地: 港
投稿日時: 2003-11-28 11:09
設計した本人でないと分からないと思いますが、憶測で・・・。まぁ、眉につばをつけつつ読んでください。

おそらく、List と Collectionの概念の違いのためだと思います。Collectionはフレームワークの一番基礎となっている部分です。Collectionはとても抽象化されていて、コレクションにオブジェクトをaddしたりするのはListやSetにaddするのとは違う意味を持つような気がします。

Sub-interfaceがSuper-interfaceのメソッドと同じメソッドを宣言するのはOverrideです。つまり、すでに宣言されているメソッドをOverrideすることにより「同じメソッド名だけど実際の意味はちょっと違いますよ」というのを強調している感じがします。意識的にしろ、無意識的にしろ、設計者の意図はそういった概念的なものではないでしょうか。実際SetもCollectionのメソッドをOverrideしています。

2番目の型の冗長ですが、これも概念的なものだと思います。おそらく、ArrayListはそれ自体がListの実装であり、冗長な型宣言をすることでArrayListはListとより近いものであると言っているよう気が私はします。もしArrayListがListを直接implementしなかった場合、ArrayListはAbstractListのサブクラスのような印象を受けますが、冗長的な宣言をすることにより、ArrayListはListであると強調している感じがします。

とまぁ、勝手な想像をしてみました。ここら辺は仕様によって決められていないので個人の概念の考え方で決まっているような気がします。実質的に問題はないですし、私よりもよりオブジェクト指向のエキスパート達が考えたもののはずなので、あまり気にはしませんけどね。

かずくん
ぬし
会議室デビュー日: 2003/01/08
投稿数: 759
お住まい・勤務地: 太陽系第三惑星
投稿日時: 2003-11-28 12:32
引用:

(2) また、もうひとつの類似の疑問としては、
つぎのように、ArrayList は AbstractList のサブクラスですが、
List も implements しています。


私も眉唾です。一切の証拠はありません。

ArrayListの設計者(Joshua Bloch ?)はイメージとして、AbstractListは実装のみを継承し、Listからはインターフェースのみを継承するということを言いたいのではないのでしょうか?

C++流にかくと
class ArrayList : public List private AbstractList {
}
ってな感じで。残念ながら、Javaにはprivate継承に相当するものがないため、今の形にあたのでは。
「そのクラスを実装の詳細とする」ことに関して、オブジェクトコンポジションという方法もありますが、抽象クラスをインスタンス化することはできないので、はなから却下されたと。

憶測なので、丸々信じないでください。

[ メッセージ編集済み 編集者: かずくん 編集日時 2003-11-28 12:33 ]
かずくん
ぬし
会議室デビュー日: 2003/01/08
投稿数: 759
お住まい・勤務地: 太陽系第三惑星
投稿日時: 2003-11-28 12:45
引用:

(2) また、もうひとつの類似の疑問としては、
つぎのように、ArrayList は AbstractList のサブクラスですが、
List も implements しています。


実は単に、当初AbstractListはListから実装されていなかった。そのため、
public class ArrayList extends AbstractList implements List....
となった。けど、あとで、AbstractListはListから派生したほうが良いかなとなったが、
もはや、互換性のため、クラス定義を変更することができず、今のままで放置されている。

な〜んてことはまずないわな(あったらコワイ)。

びしばし
大ベテラン
会議室デビュー日: 2002/03/13
投稿数: 181
投稿日時: 2003-11-28 14:14
「私はこうだと思う」論の追加。

AbstractList に限らず、AbstractMap (implements Map) のサブクラス(HashMap など)も改めて implements Map としているところを見ると、単純に

「サブクラス(ArrayList など)の定義だけ見て List インターフェイスを持っていることがぱっと見ですぐわかるから。」

なだけな気がします。

swing の Abstract* ではそうなってないですけどね。
Emacs信者
常連さん
会議室デビュー日: 2003/08/10
投稿数: 38
投稿日時: 2003-11-28 15:10
引用:

かずくんさんの書き込み (2003-11-28 12:32) より:
ArrayListの設計者(Joshua Bloch ?)はイメージとして、AbstractListは実装のみを継承し、Listからはインターフェースのみを継承するということを言いたいのではないのでしょうか?



僕としては、かずくんさんの意見に賛成です。
ただ、もし実装のための継承という意図があったのなら、こうした方がよかったのかな、とは思いますが。
コード:

abstract class PackagePrivateAbstractList implements List{
//Listのスケルトン実装
}

public abstract class AbstractList extends PackagePrivateAbstractList{
//空っぽの実装
}

public class ArrayList
extends PackagePrivateAbstractList
implements List{
//実装だけを継承
}




[ メッセージ編集済み 編集者: Emacs信者 編集日時 2003-11-28 15:15 ]
nil
会議室デビュー日: 2003/06/17
投稿数: 14
投稿日時: 2003-11-29 10:56
引用:

コード:
abstract class PackagePrivateAbstractList implements List{
    //Listのスケルトン実装
}

public abstract class AbstractList extends PackagePrivateAbstractList{
    //空っぽの実装
}





こんなこになってしまったら、AbstractListの実装が不明になってしまい、
効率よいListの作成のためにどのメソッドを記述すべきか分からなくなってしまいますね。

スキルアップ/キャリアアップ(JOB@IT)