polymorphism まとめ
Collection Framework の利用事例を紹介しつつ、クラス階層の使われ方について見てきました。
クラス階層、インタフェイスの実装に関して、ポイントをまとめておきましょう。
- あるクラスは、親クラスや実装インタフェイスの機能を持っている(こういう性質をサブタイプと言います)。
- だから、そのクラスのオブジェクトが、親クラスやインタフェイスとして扱われても構わない。
- 逆に、クラスやインタフェイスで、子クラスやインタフェイスを実装するクラスを取りまとめて扱える。
- 事例(親と子の順で)
Object#toString()
メソッドと、TreeSet
,ArrayList
,Integer
,MyPoint
における再定義Collection<Integer>
インタフェイスと、ArrayList<Integer>
やTreeSet<Integer>
Comparator<MyPoint>
インタフェイスと、MyPointComp0
や anonymous inner class の事例Comparable<Integer>
インタフェイスとInteger
の事例
- よくある利用例
- ライブラリと利用側の橋渡し。特に、ライブラリから、利用者コードを呼ぶために利用。
- 事例1:
toString()
の例- ライブラリ提供側: 例えば
TreeSet
のtoString()
を実現する際、要素elem
の表示にはelem.toString()
を呼ぶ - 利用者側:自作クラスで独自の
toString()
を定義しておく
- ライブラリ提供側: 例えば
- 事例2:
Comparator<T>
の例- ライブラリ提供側: 例えば
TreeSet
のソート機能は、要素比較の際は与えられたComparator<T>
のcompare()
を呼ぶ - 利用者側:自分の用途にあった
Comparator
を定義すれば、ソート順を制御可能
- ライブラリ提供側: 例えば
余談:サブタイプ
型パラメータに関する、サブタイプではない例を紹介しておきましょう。 ちょっと込み入った話なので、興味ある人にだけ。
まずですが、サブタイプとは?ですが、
B
がA
のサブタイプとは、B
がA
の代わりに使える と言うことです。- Java の場合、サブクラスは、親クラスの代わりに使えるので、
B extends A
なら、B
はA
のサブタイプです。 - 基本的に、この世界では「サブ」の方が「高機能」な訳です。
でも、ArrayList<Integer>
と ArrayList<Object>
は、互いにサブタイプではないんです。
つまり、互いに代役をこなせないんです。
ArrayList<Integer> ilist;
ArrayList<Object> olist;
olist.add(new Object()); //当然OK。
ilist.add(new Object()); //(コンパイル)エラー。つまり、ilist は olist の代わりはできない。
ilist.get(0).intValue(); // intValue() は、Integer クラスのメソッドで、OK.
olist.get(0).intValue(); // (コンパイル)エラー。つまり、olist は ilist の代わりはできない。
一般に、メソッド/関数の場合、B が A のサブタイプの場合、
B foo(String)
は、A foo(String)
の代わりに使えて、 (B A の サブタイプ関係保存)String foo(A)
は、String foo(B)
の代わりに使える。 (B A の サブタイプ関係と逆)
となります。
一方で、ArrayList<Integer>
, ArrayList<Object>
を取りまとめて扱いたいときもあります。そんな時のために、
ArrayList<? extends Object> xlist; // Objectを継承した「とあるクラス」を要素にもつArrayList
という型も存在します。でも、引数に型パラメータがくるようなものは利用できなくなります。
xlist = new ArrayList<Integer>(); // OK
Object o = xlist.get(0); // OK。取得した要素は、Object のサブクラスなので、Object として扱える。
xlist.add(new Object()); // 要素の型は ? で、Object は ? のサブクラスじゃないので、型エラー
という使い方をします。