本カテゴリーは手続き型言語になれている人がどうやったらオブジェクト指向を理解できるのかを考えて書いている記事です。
多少の言語知識があることを前提に進めます。
さて、今回は継承とポリモルフィズムの説明をしようと思います。
ここでのポイントは、継承とポリモルフィズムを同時に学ぶところです。
継承と言うと元々ある機能を使って拡張するイメージが強いのですが、オブジェクト指向での利点はポリモルフィズムにあると思います。
まぁ、これは主観ですが。
あ、始める前にもう一点。
継承とポリモルフィズムは設計が肝心です。
継承もポリモルフィズムも言語仕様としてはそんなに難しい物ではありません。
難しいのはその機能をいかに使えばよいかと言う思考なのです。
では始めましょう。
前回作ったクラスはこんな感じでしたね。(*1)
public Sankaku{ private int tyoutenAX; private int tyoutenAY; private int tyoutenBX; private int tyoutenBY private int tyoutenCX; private int tyoutenCY; // 頂点Aを設定するメソッド public void SetTyoutenA(int x, int y) { this.tyoutenAX = x; this.tyoutenAY = y; } // 頂点Bを設定するメソッド public void SetTyoutenB(int x, int y) { this.tyoutenBX = x; this.tyoutenBY = y; } // 頂点Cを設定するメソッド public void SetTyoutenC(int x, int y) { this.tyoutenCX = x; this.tyoutenCY = y; } } |
これを使っているコードがこんな感じでしたね。
public static void main() { // 三角形一つ一つを配列として定義して考える。 Sankaku sankakuA = new Sankaku(); Sankaku sankakuB = new Sankaku(); Sankaku sankakuC = new Sankaku(); // 三角形には三点の頂点があるため、三つの頂点を設定する。 // データ設定 sankakuA.SetTyoutenA(100, 100); sankakuA.SetTyoutenB(50, 50); sankakuA.SetTyoutenC(50, 100); // 以下、あと二つの三角の頂点を設定する。 sankakuBの設定 sankakuCの設定 // 実際に三角を表示する。 sankakuWrite(sankakuA); sankakuWrite(sankakuB); sankakuWrite(sankakuC); } // 三角を描画する関数 // 関数に全てのデータが入っているため引数が一つになる。 public static void sankakuWrite(Sankaku sankaku) { // 頂点を元に三角形を書く関数 } |
今のままではまったく継承やポリモルフィズムを使う必要がありません。
ここでは一つ仕様を増やしてみましょう。
三角形以外に四角形も描画するようにして欲しい。 |
このように近いデータ構造を持っている物は継承に適しています。
また、近い処理を持っている物はポリモルフィズムに適しています。
まぁ、まずは継承から入りましょう。
三角形とは違い四角形は頂点が一つ多いだけです。
このようなクラスを作れば上手く行きそうです。
public Sikaku{ private int tyoutenAX; private int tyoutenAY; private int tyoutenBX; private int tyoutenBY private int tyoutenCX; private int tyoutenCY; private int tyoutenDX; private int tyoutenDY; // 頂点Aを設定するメソッド public void SetTyoutenA(int x, int y) { this.tyoutenAX = x; this.tyoutenAY = y; } // 頂点Bを設定するメソッド public void SetTyoutenB(int x, int y) { this.tyoutenBX = x; this.tyoutenBY = y; } // 頂点Cを設定するメソッド public void SetTyoutenC(int x, int y) { this.tyoutenCX = x; this.tyoutenCY = y; } // 頂点Dを設定するメソッド public void SetTyoutenD(int x, int y) { this.tyoutenDX = x; this.tyoutenDY = y; } } |
って、なんだか三角形のクラスにそっくりですね。
ちょっと、データが拡張しただけですよね。
だったらさっきから言っている継承を使ってみましょう。
とりあえず、ソース
public Sikaku : Sankaku{ private int tyoutenDX; private int tyoutenDY;
// 頂点Dを設定するメソッド public void SetTyoutenD(int x, int y) { this.tyoutenDX = x; this.tyoutenDY = y; } } |
このように三角形のクラスを使用することによって同じロジックは二回も書かなくてすみます。
(継承の書き方は言語によって違います。この記述方法はC#の記述方法で、":"の後に継承元のクラスを記述すれば継承されます。)
これが継承の利点・・・と言うか、オブジェクト指向の利点である
コードの再利用
です。
ここで、いくつか補足します。
SikakuクラスからSankakuクラスにアクセスできるのはSetTyoutenメソッドだけです。
つまり、このままではSankakuクラスで格納しているデータを取り出すことが出来ません。(*2)
では、
何故データを取り出せないか考えてください
また、
どうすればデータを取り出せるか考えてください
ヒントはprotectedキーワードです。
次に継承した場合の呼び出し方です。
public static void main() { // newキーワードは宣言時に指定することも可能。 Sikaku sikakuA = new Sikaku(); // データ設定 sikakuA.SetTyoutenA(100, 100); sikakuA.SetTyoutenB(100, 50); sikakuA.SetTyoutenC(50, 100); sikakuA.SetTyoutenD(50, 50); } |
三角形と同じであまり難しくないですね。
このように継承した物だけ考えると、あまり難しくないのですが・・・
さて、このようなコードを書いた場合はエラーになるでしょうか?
public static void main() { // newキーワードは宣言時に指定することも可能。 Sankaku sikakuA = new Sikaku(); // データ設定 sikakuA.SetTyoutenA(100, 100); sikakuA.SetTyoutenB(100, 50); sikakuA.SetTyoutenC(50, 100); ((Sikaku)sikakuA).SetTyoutenD(50, 50); } |
このように考えると難しくなるのです。
何でわざわざ難しい書き方を持ってきたかというと、ポリモルフィズムの理解に必要だからです。
このコードはエラーにはなりません。
宣言は三角にも関わらず、実態は四角という事が出来るのです。
なお、三角のクラスには四角のデータは入れれません。
なので最終行では一度キャスト(*3)を行ってデータを入れています。
まぁ、ここの考え方は次回ゆっくりやりましょう。
次回はスタック領域とヒープ領域について話そうと思います。
この辺りはオブジェクト指向でも肝になる部分だと思いますので、じっくりと書きますのでお楽しみに。
※sankakuWriteクラスで普通に描画処理を行おうと思っていたのですが、、、考えてみたら頂点座標が取り出せませんでしたね・・・。申し訳ないですが、ここでは無理やり画像処理を行っていると認識しておいてください。
-------------------------------
*1・・・クラスがprivateで定義されていたが誤りです。publicに直しました。
*2・・・実はprivate変数でもポインタの位置さえ合わせてしまえば無理やりデータを引き出したり設定したりすることは可能です。
*3・・・キャストはC手続き型言語にもありましたよね?long型をint型にキャストとか。ただし、long→intの場合にint型に収まりきれないデータは削られましたが、クラスのキャストではそのようなことはありません。