第10章 継承とオーバーライド
10.1 継承のしくみを知る
実際のソースコードでどのように記述すれば、拡張したクラスを作成できるのか説明していきます。
10.2.1 クラスを拡張する方法について
まずはクラスを拡張する基本構文を以下に示します。
書式:クラスの拡張
凡例:クラスの拡張
クラスを拡張する方法はそれほど難しくありません。凡例を見ても分かるように新しいクラス名の横に「extends継承元のクラス名」と記述すれば、これだけでNotePcクラスを拡張しComputeクラスの機能全てを受け継ぐこと(継承)が可能になります。後はComputerクラスにはない、NotePcクラスに必要なメンバを記述すればよいのです。
拡張を行ったことで若干記述方法に制約がつく場合と、基本クラスを扱う仕組みが増えてはいますが、基本的なクラスの定義方法はこれまで学習してきた方法と変わりありません。制約がつく場合と基本クラスを扱う仕組みについては後程解説を行っていきます。
ポイント・クラスの宣言時にクラスを拡張し基本クラスを指定することができる。作られたクラスは派生クラスとなり、基本クラスのメンバを継承する。
それでは実際に既存のクラスを拡張して新しいクラスを作成したプログラムを紹介していきます。
10.2.2 クラスを拡張するプログラム
既存のクラス「Computer1」の機能を継承した新しいクラス「NotePc1」を作成することを通して、クラスを拡張する方法について学習しましょう。
ソースコード
ソース・フォルダー :myproj_framework_basic/ch10
ファイル名 :computer1.php
アクセスURL :今回は作成するだけなのでアクセスしない
➢ computer1.php
<?php // 基本クラスの定義作成 class Computer1{ private $os; private $memory; // コンストラクタ public function __construct(){ $this->os = "OS未設定"; $this->memory = 0; echo "パソコンを作成しました。<br>"; } public function setOsMemory($os, $memory){ $this->os = $os; $this->memory = $memory; echo "OSを「" . $os . "」に、メモリサイズを「" . $memory . "MByte」に変更しました。<br>"; } public function show(){ echo "パソコンのOSは「" . $this->os . "」です。<br>"; echo "メモリサイズは「" . $this->memory . "MByte」です。<br>"; } } ?>
ソース・フォルダー :myproj_framework_basic/ch10
ファイル名 :notePc1.php アクセスURL :今回は作成するだけなのでアクセスしない
➢ notePc1.php
<?php // 派生クラスの定義作成 class NotePc1 extends Computer1{ private $useType; //用途タイプ // コンストラクタ public function __construct(){ $this->useType = "用途タイプ未設定"; echo "ノートパソコンを作成しました。<br>"; } public function setUseType($useType){ $this->useType = $useType; echo "タイプは「" . $useType . "」用にしました。<br>"; } public function showUseType(){ echo "タイプは「" . $this->useType . "」用です。<br>"; } } ?>
実行結果
既存のクラスの機能を継承した新しいクラスを定義しただけの為、実行結果は無し
解説
継承元のComputer1クラスは基本クラスとなり、NotePc1クラスはComputer1クラスの機能を継承した派生クラスになります。
notePc1.phpファイルの3行目のクラス名の横に「extends Computer1」と記述することでComputer1クラスを拡張しています。
4行目にはこのクラスで利用する、フィールド変数$useTypeを定義しています。
7~10行目はこのクラスのコンストラクタを定義しています。
11~17行目ではインスタンスメソッドのsetUseTypeメソッドとshowUseTypeメソッドを定義しています。
3行目のクラス名NotePc1の横に拡張する為の宣言があるだけで、クラスの定義方法は変わっていないことが確認できます。これでComputer1クラスの機能を継承したNotePc1クラスが作成できたことになります。
図 10.2.1: クラスの拡張と継承
次項で拡張して定義した新しいクラスから、継承した機能や自身の機能を利用したプログラムを紹介します。
10.2.3 派生クラスのオブジェクトを作成して利用するプログラム
拡張して作成した派生クラスをオブジェクト化して、継承した機能と独自の機能を利用して動作することを確認します。
ソースコード
➢ computer1.php ※基本クラス
10.2.2で作成したクラスを利用します。
➢ notePc1.php ※派生クラス
10.2.2で作成したクラスを利用します。
ソース・フォルダー :myproj_framework_basic/ch10
ファイル名 :useDerivedClass1.php
アクセスURL :http://localhost/myproj_framework_basic/ch10/useDerivedClass1.php
➢ useDerivedClass1.php ※オブジェクトを生成して実行するクラス
<?php //基本クラスと派生クラスの読み込み require_once 'computer1.php'; require_once 'notePc1.php'; ?> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>派生クラスのオブジェクト利用</title> </head> <body> <?php // 派生クラスのオブジェクトを生成 $npc = new NotePc1(); // 基本クラスのshow()メソッド呼び出し(1回目) $npc->show(); // 派生クラスのshowUseTypeメソッド呼び出し(1回目) $npc->showUseType(); // 基本クラスのメンバ呼び出し $npc->setOsMemory("WindowsXP", 1024); // 派生クラスのメンバ呼び出し $npc->setUseType("モバイル"); // 基本クラスのshow()メソッド呼び出し(2回目) $npc->show(); // 派生クラスのshowUseTypeメソッド呼び出し(2回目) $npc->showUseType(); ?> </body> </html>
実行結果
解説
まず2~3行目で、別のファイルに定義されているComputer1クラスとNotePc1クラスを使えるように、ファイル読み込みを行っています。
14行目でComputer1クラスの機能を継承したNotePc1クラスのオブジェクト化を行っています。
拡張したクラスであっても、これまで通りnew演算子を利用してオブジェクト生成できます。
17、20行目で基本クラスのshowメソッドと派生クラスのshowUseType()メソッドを呼び出し、パソコン情報として各フィールド変数の値を出力しています。その結果として、派生クラスであるNotePc1クラスのコンストラクタで初期値が設定されている$useType以外のフィールド変数は空文字の状態で画面出力されます。
23行目で基本クラスのsetOsMemory()メソッドを呼び出して各パソコン情報の各値を設定しています。
12行目で拡張して追加した自身のsetUseType()メソッドを利用して用途タイプを設定しています。
29、32行目で基本クラスのshowメソッドと派生クラスのshowUseType()メソッドを呼び出して再度パソコン情報として各フィールド変数の値を出力しています。
実行結果からも分かるように、基本クラスから継承したメソッドや派生クラスで拡張したメソッドを同じオブジェクトから呼び出せていることが確認できます。
図 10.2.2: 派生クラスのオブジェクトを作成して利用するプログラムのクラス構成
ポイント・派生クラスは基本クラスのフィールドとメソッドを引き継ぎ、さらに機能を追加できる。
今回のプログラムの実行結果から気になる点があります。それは今回のプログラムではNotePc1クラスのオブジェクト化のみを行っているので、継承元のComputer1クラスのコンストラクタは呼び出されていない点です。フィールド変数は使うことはできるのに、継承元のコンストラクタで行っている初期化処理を流用できないのは大変不便です。
そこで、派生クラスのコンストラクタで基本クラスのコンストラクタを呼び出す方法を次の項で紹介します。
10.2.4 基本クラスのコンストラクタを派生クラスで利用する方法
基本クラスのコンストラクタを派生クラスのコンストラクタで呼び出す場合は、以下の構文で呼び出します。
書式:基本クラスのコンストラクタ呼び出し
コンストラクタを呼び出すときの__construct()メソッドの前に「parent::」を付けることによって、継承元の基本クラスで定義したコンストラクタを呼び出すことができます。この時値が設定されるのは基本クラスのオブジェクトではなく、あくまで派生クラスのオブジェクト内にあるフィールド変数であることに注意しましょう。
10.2.5 基本クラスのコンストラクタを派生クラスで利用するプログラム
実際に基本クラスのコンストラクタを派生クラスのコンストラクタで呼び出してみます。
ソースコード
➢ computer1.php ※基本クラス
10.2.2で作成したクラスを利用します。
ソース・フォルダー :myproj_framework_basic/ch10
ファイル名 :notePc2.php
アクセスURL :このファイルは直接実行しない
➢ computer1.php ※基本クラス
<?php // 派生クラスの定義作成 class NotePc2 extends Computer1{ private $useType; //用途タイプ // コンストラクタ public function __construct(){ parent::__construct(); $this->useType = "用途タイプ未設定"; echo "ノートパソコンを作成しました。<br>"; } public function setUseType($useType){ $this->useType = $useType; echo "タイプは「" . $useType . "」用にしました。<br>"; } public function showUseType(){ echo "タイプは「" . $this->useType . "」用です。<br>"; } } ?>
解説
NotePc1とほぼ同じ記述ですが、8行目の処理がコンストラクタに追加されています。これによって、NotePc2クラスのオブジェクト生成時に、継承元であるComputer1クラスで定義したコンストラクタを実行してから、NotePc2クラスのコンストラクタの処理を行なうことができます。
ソース・フォルダー :myproj_framework_basic/ch10
ファイル名 :useDerivedClass2.php
アクセスURL :http://localhost/myproj_framework_basic/ch10/useDerivedClass2.php
➢ useDerivedClass2.php ※オブジェクトを生成して実行するファイル
<?php //基本クラスと派生クラスの読み込み require_once 'computer1.php'; require_once 'notePc2.php'; ?> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>基本クラスのコンストラクタ利用</title> </head> <body> <?php // 派生クラスのオブジェクトを生成 $npc = new NotePc2(); // 基本クラスのshow()メソッド呼び出し(1回目) $npc->show(); // 派生クラスのshowUseTypeメソッド呼び出し(1回目) $npc->showUseType(); ?> </body> </html>
実行結果
解説
2~3行目で、別のファイルに定義されているComputer1クラスとNotePc2クラスを使えるように、ファイル読み込みを行っています。
14行目で新たに作成した派生クラスであるNotePc2クラスのオブジェクト化を行っています。
17、20行目で基本クラスのshowメソッドと派生クラスのshowUseType()メソッドを呼び出し、パソコン情報として各フィールド変数の値を出力しています。その結果として、派生クラスであるNotePc1クラスのコンストラクタで初期値が設定されている$useType以外のフィールド変数についても、Computer1クラスのコンストラクタでの初期値が設定された状態で画面出力されます。
実行結果からも、基本クラスのコンストラクタが先に実行されてから派生クラスのコンストラクタが実行されていることがわかります。
この様に、基本クラスのコンストラクタを派生クラスのコンストラクタで呼び出すことで、より効率的に値の設定を行うことができます。
抽象クラスとインターフェース
複数のクラスで同時に拡張することはできませんが、拡張されたクラスを元にさらに新しいクラスを拡張をすることはできます。これによって、クラスの階層を構成することができます。
PHPにはクラス階層を構成するために、抽象クラス(abstract class)とインターフェース(interface)という仕組みを備えています。抽象クラスとインターフェースは、クラスと似た記述で、公開メソッドの名前だけを宣言しておく仕組みとなっています。名前だけ決めて、具体的な処理はそれらを元に実装された新しいクラスの方で決めてもらう、という考え方になっています。