「オーバーライド」とは?
状態:-
閲覧数:6,156
投稿日:2010-04-29
更新日:2013-10-30
親クラスに存在するプロパティ/メソッドと同じ名前のプロパティ/メソッドを、子クラスで再定義すること
・継承するクラスで、親クラスのプロパティ、メソッドを上書きすること
・例えば、メソッドの場合、親クラスから継承したメソッドと同じ名前の関数を、子クラスで再定義すると、動作を上書きすることが可能になる
・親クラスのメソッドに追加機能を持たせたい場合、または親クラスのメソッドの機能を無効にしたい場合などに利用
・再定義(動作を上書き)と言っても、何か特別な記述をするわけではない
・単に、親クラスに存在するプロパティ/メソッドと同じ名前のプロパティ/メソッドを、子クラスで作成するだけ
・「親クラス」「基底クラス」は、同義
・「子クラス」「派生クラス」は、同義
・「メソッド」「メンバ関数」は、同義
・「プロパティ」「メンバ変数」「フィールド」「属性」は、ここでは同義
「クラス内でのみ参照可能なprivateメンバ」を、子クラスで上書きしても、親クラスからは参照できない(private制限により)
・この際、下記何れかに該当すると思われるのだが、詳細不明
a.オーバーライド自体は行なわれたが、親クラスのprivateメンバ制限が有効
b.オーバーライド自体行なわれていない。親クラスのprivateメンバ制限は有効
c.再定義ではなく新定義を行なったが、親クラスのprivateメンバ制限が有効
※要は、privateメンバは継承されていないのに、再定義扱いとなるかが分からない。ちなみに、この際通常レベルエラーは出ない
・何れにしても、現象として、子クラスで「privateプロパティ」を上書きした場合は、次のようになる
子クラス内$thisから見た場合
・privateプロパティを新定義したのと同じ扱い
親クラス内$thisから見た場合
・子クラスのprivate上書きプロパティはないのと同じ(継承されずに、親クラスプロパティが優先される)
・継承するクラスで、親クラスのプロパティ、メソッドを上書きすること
・例えば、メソッドの場合、親クラスから継承したメソッドと同じ名前の関数を、子クラスで再定義すると、動作を上書きすることが可能になる
利用目的
・親クラスのメソッドに追加機能を持たせたい場合、または親クラスのメソッドの機能を無効にしたい場合などに利用
書式
・再定義(動作を上書き)と言っても、何か特別な記述をするわけではない
・単に、親クラスに存在するプロパティ/メソッドと同じ名前のプロパティ/メソッドを、子クラスで作成するだけ
同義語
・「親クラス」「基底クラス」は、同義
・「子クラス」「派生クラス」は、同義
・「メソッド」「メンバ関数」は、同義
・「プロパティ」「メンバ変数」「フィールド」「属性」は、ここでは同義
注意点
「クラス内でのみ参照可能なprivateメンバ」を、子クラスで上書きしても、親クラスからは参照できない(private制限により)
・この際、下記何れかに該当すると思われるのだが、詳細不明
a.オーバーライド自体は行なわれたが、親クラスのprivateメンバ制限が有効
b.オーバーライド自体行なわれていない。親クラスのprivateメンバ制限は有効
c.再定義ではなく新定義を行なったが、親クラスのprivateメンバ制限が有効
※要は、privateメンバは継承されていないのに、再定義扱いとなるかが分からない。ちなみに、この際通常レベルエラーは出ない
・何れにしても、現象として、子クラスで「privateプロパティ」を上書きした場合は、次のようになる
子クラス内$thisから見た場合
・privateプロパティを新定義したのと同じ扱い
親クラス内$thisから見た場合
・子クラスのprivate上書きプロパティはないのと同じ(継承されずに、親クラスプロパティが優先される)
メソッドオーバーライド
子クラスインスタンス生成
ケース1
・親クラスプロパティprotected
・子クラスプロパティprotected
class Oya{
protected $price="100";
public function getPrice(){ // ゲッター
echo "親クラスメソッド",PHP_EOL;
return $this->price;
}
}
class Ko extends Oya{
protected $price="50"; // プロパティをオーバーライド
public function getPrice(){ // ゲッター
echo "子クラスメソッド<br />",PHP_EOL;
return $this->price;
}
}
$ko = new Ko();//子クラスインスタンスオブジェクトを生成
echo '子クラスインスタンスオブジェクトのプロパティ' . $ko->getPrice(),PHP_EOL;
// 処理結果
// 子クラスメソッド
// 子クラスインスタンスオブジェクトのプロパティ50
ケース2
・親クラスプロパティprotected
・子クラスプロパティprotected(parent利用)
→子クラスインスタンスオブジェクトのプロパティを、子クラスメソッド→親クラスメソッド経由で取得(処理的には遠回りで意味がない)
class Oya{
protected $price="100";
public function getPrice(){ // ゲッター
echo "親クラスメソッド<br />",PHP_EOL;
return $this->price;
}
}
class Ko extends Oya{
protected $price="50"; // プロパティをオーバーライド
public function getPrice(){ // ゲッター
echo "子クラスメソッド",PHP_EOL;
return parent::getPrice();
}
}
$ko = new Ko();//子クラスインスタンスオブジェクトを生成
echo '子クラスインスタンスオブジェクトのプロパティ(子クラスメソッド→親クラスメソッド経由で取得)' . $ko->getPrice(),PHP_EOL;
// 処理結果
// 子クラスメソッド 親クラスメソッド
// 子クラスインスタンスオブジェクトのプロパティ(子クラスメソッド→親クラスメソッド経由で取得)50
ケース3
・親クラスプロパティprivate
・子クラスプロパティprotected(parent利用)
→子クラスインスタンスオブジェクトのプロパティを、子クラスメソッド→親クラスメソッド経由で、親クラスプロパティより取得
→子クラスインスタンスオブジェクトのプロパティを、子クラスメソッド経由で、子クラスプロパティより取得
class Oya{
private $price="100";
public function getPrice(){ // ゲッター
echo "親クラスメソッド<br />",PHP_EOL;
return $this->price;
}
}
class Ko extends Oya{
protected $price="50";
public function getParentprice(){ // ゲッター
echo "子クラスメソッドgetParentprice",PHP_EOL;
return parent::getPrice();
}
public function getPrice(){ // ゲッター
echo "<br />子クラスメソッドgetPrice<br />",PHP_EOL;
return $this->price;
}
}
$ko = new Ko();//子クラスインスタンスオブジェクトを生成
echo '子クラスインスタンスオブジェクトプロパティ(子クラスメソッド→親クラスメソッド経由で、親クラスプロパティより取得)' . $ko->getParentprice(),PHP_EOL;
echo '子クラスインスタンスオブジェクトプロパティ(子クラスメソッド経由で、子クラスプロパティより取得)' . $ko->getPrice(),PHP_EOL;
// 処理結果
// 子クラスメソッドgetParentprice 親クラスメソッド
// 子クラスインスタンスオブジェクトプロパティ(子クラスメソッド→親クラスメソッド経由で、親クラスプロパティより取得)100
// 子クラスメソッドgetPrice
// 子クラスインスタンスオブジェクトプロパティ(子クラスメソッド経由で、子クラスプロパティより取得)50
親子クラスインスタンス生成
・「親クラス」「子クラス」それぞれのインスタンスを生成
ケース4
・親クラスプロパティprivate
・子クラスプロパティprotected
→親クラスインスタンスオブジェクトなら親クラスプロパティを、子クラスインスタンスオブジェクトなら子クラスプロパティを呼び出す
class Oya{
private $price="100";
public function getPrice(){ // ゲッター
echo "親クラスメソッド<br />",PHP_EOL;
return $this->price;
}
}
class Ko extends Oya{
protected $price="50";
public function getPrice(){ // ゲッター
echo "<br />子クラスメソッド<br />",PHP_EOL;
return $this->price;
}
}
$oya = new Oya();//親クラスのインスタンスオブジェクトを生成
$ko = new Ko();//子クラスのインスタンスオブジェクトを生成
echo '親クラスのインスタンスオブジェクトプロパティ' . $oya->getPrice(),PHP_EOL;
echo '子クラスのインスタンスオブジェクトプロパティ' . $ko->getPrice(),PHP_EOL;
// 処理結果
// 親クラスメソッド
// 親クラスのインスタンスオブジェクトプロパティ100
// 子クラスメソッド
// 子クラスのインスタンスオブジェクトプロパティ50
ケース5
・親クラスプロパティprotected
・子クラスプロパティprotected
→親クラスインスタンスオブジェクトなら親クラスプロパティを、子クラスインスタンスオブジェクトなら子クラスプロパティを呼び出す
class Oya{
protected $price="100";
public function getPrice(){ // ゲッター
echo "親クラスメソッド",PHP_EOL;
return $this->price;
}
}
class Ko extends Oya{
protected $price="50"; // プロパティをオーバーライド
public function getPrice(){ // ゲッター
echo "<br />子クラスメソッド",PHP_EOL;
return $this->price;
}
}
$oya = new Oya();//親クラスのインスタンスオブジェクトを生成
$ko = new Ko();//子クラスのインスタンスオブジェクトを生成
echo '親クラスのインスタンスオブジェクトプロパティ' . $oya->getPrice(),PHP_EOL;
echo '子クラスのインスタンスオブジェクトプロパティ' . $ko->getPrice(),PHP_EOL;
// 処理結果
// 親クラスメソッド 親クラスのインスタンスオブジェクトプロパティ100
// 子クラスメソッド 子クラスのインスタンスオブジェクトプロパティ50
ケース6
・親クラスプロパティprivate
・子クラスプロパティprivate
→親クラスインスタンスオブジェクトなら親クラスプロパティを、子クラスインスタンスオブジェクトなら子クラスプロパティを呼び出す
class Oya{
private $price="100";
public function getPrice(){ // ゲッター
echo "親クラスメソッド",PHP_EOL;
return $this->price;
}
}
class Ko extends Oya{
private $price="50";
public function getPrice(){ // ゲッター
echo "<br />子クラスメソッド",PHP_EOL;
return $this->price;
}
}
$oya = new Oya();//親クラスのインスタンスオブジェクトを生成
$ko = new Ko();//子クラスのインスタンスオブジェクトを生成
echo '親クラスのインスタンスオブジェクトプロパティ' . $oya->getPrice(),PHP_EOL;
echo '子クラスのインスタンスオブジェクトプロパティ' . $ko->getPrice(),PHP_EOL;
// 処理結果
// 親クラスメソッド 親クラスのインスタンスオブジェクトプロパティ100
// 子クラスメソッド 子クラスのインスタンスオブジェクトプロパティ50
複雑な例
parent先メソッド内の「this指定先プロパティ」がオーバーライド
class Oya{
private $price="100"; // 価格
protected $netprice="50000"; // 価格
public function getPrice(){ // ゲッター。価格を取得
return $this->price;
}
public function getNetprice(){ // ゲッター。価格を取得
return $this->netprice;
}
public function setPrice($price){ // セッター。価格を設定
$this->price = $price;
}
}
class Ko extends Oya{
private $price="50"; // 価格
protected $netprice="1250"; // 価格
public function getParentprice(){
return parent::getNetprice();
}
public function getPrice(){ // ゲッター。価格を取得
return $this->price;
}
}
$ko = new Ko();//子クラスのインスタンスオブジェクトを生成
echo '子クラスインスタンスオブジェクトプロパティ(新定義した子クラスプロパティより取得)' . $ko->getPrice();
$oya = new Oya();//親クラスのインスタンスオブジェクトを生成
$oya->setPrice(200);//親クラスインスタンスオブジェクトプロパティを200にセット
echo "<br />親クラスインスタンスオブジェクトプロパティ(親クラスプロパティより取得)".$oya-> getPrice();
echo '<br />子クラスインスタンスオブジェクトプロパティ(新定義した子クラスプロパティより取得)' . $ko->getPrice();
echo "<br />子クラスインスタンスオブジェクトのnetpriceプロパティ(子クラスgetParentpriceメソッド→親クラスgetNetpriceメソッド→親クラスnetpriceプロパティ→子クラスnetpriceプロパティの流れで、最終的に子クラスnetpriceプロパティより取得)".$ko->getParentprice();//親クラスメソッドをgetParentprice()メソッド内のparentで指定して呼び出している。親クラスgetPrice()メソッド内の$thisは、親クラスプロパティ$netpriceを指すが、同プロパティは継承上書きされているため、最終的に子クラスプロパティが返ってくる(取得することになる)
// 処理結果
// 子クラスインスタンスオブジェクトプロパティ(新定義した子クラスプロパティより取得)50
// 親クラスインスタンスオブジェクトプロパティ(親クラスプロパティより取得)200
// 子クラスインスタンスオブジェクトプロパティ(新定義した子クラスプロパティより取得)50
// 子クラスインスタンスオブジェクトのnetpriceプロパティ(子クラスgetParentpriceメソッド→親クラスgetNetpriceメソッド→親クラスnetpriceプロパティ→子クラスnetpriceプロパティの流れで、最終的に子クラスnetpriceプロパティより取得)1250
parent先メソッド内の「this指定先プロパティ」がprivateのためオーバーライドされていない
class Oya{
private $price="100"; // 価格
private $netprice="50000"; // 価格
public function getPrice(){ // ゲッター。価格を取得
return $this->price;
}
public function getNetprice(){ // ゲッター。価格を取得
return $this->netprice;
}
public function setPrice($price){ // セッター。価格を設定
$this->price = $price;
}
}
class Ko extends Oya{
private $price="50"; // 価格
protected $netprice="1250"; // 価格
public function getParentprice(){
return parent::getNetprice();
}
public function getPrice(){ // ゲッター。価格を取得
return $this->price;
}
}
$ko = new Ko();//子クラスのインスタンスオブジェクトを生成
echo '子クラスインスタンスオブジェクトプロパティ(新定義した子クラスプロパティより取得)' . $ko->getPrice();
$oya = new Oya();
$oya->setPrice(200);
echo "<br />親クラスインスタンスオブジェクトプロパティ(親クラスプロパティより取得)".$oya-> getPrice();
echo '<br />子クラスインスタンスオブジェクトプロパティ(新定義した子クラスプロパティより取得)' . $ko->getPrice();
echo "<br />子クラスインスタンスオブジェクトのnetpriceプロパティ(子クラスgetParentpriceメソッド→親クラスgetNetpriceメソッド→親クラスnetpriceプロパティの流れで、最終的に親クラスnetpriceプロパティより取得)".$ko->getParentprice();//親クラスメソッドをgetParentprice()メソッド内のparentで指定して呼び出している。親クラスgetPrice()メソッド内の$thisは、親クラスプロパティ$netpriceを指すため、最終的に親クラスプロパティが返ってくる(取得することになる)
// 処理結果
// 子クラスインスタンスオブジェクトプロパティ(新定義した子クラスプロパティより取得)50
// 親クラスインスタンスオブジェクトプロパティ(親クラスプロパティより取得)200
// 子クラスインスタンスオブジェクトプロパティ(新定義した子クラスプロパティより取得)50
// 子クラスインスタンスオブジェクトのnetpriceプロパティ(子クラスgetParentpriceメソッド→親クラスgetNetpriceメソッド→親クラスnetpriceプロパティの流れで、最終的に親クラスnetpriceプロパティより取得)50000
「抽象メソッド」を含まない抽象クラス。プロパティオーバーライド
abstract class Oya{
protected $login_action = array('account', 'signin');
public function run(){
print_r($this->login_action);
}
}
class Ko extends Oya{
protected $login_action = array('account1', 'signin1');
}
$ko = new Ko();//子クラスインスタンスオブジェクトを生成
$ko->run();
// 処理結果
// Array
// (
// [0] => account1
// [1] => signin1
// )
「抽象メソッド」を含まない抽象クラス。プロパティオーバーライドではない
abstract class Oya{
public function run(){
v($this->login_action);
}
}
class Ko extends Oya{
protected $login_action = array('account', 'signin');
}
$ko = new Ko();//子クラスインスタンスオブジェクトを生成
$ko->run();
// 処理結果
// Array
// (
// [0] => account
// [1] => signin
// )
継承例
・Productの機能は全て持った上で新しい機能を持たせたBookProductというクラスを作成
・親クラスゲッタ(価格取得)をオーバーライド(親クラスメソッドの上書き)
class Product{
protected $price; // 価格
public function getPrice(){ // ゲッタ(価格取得)
return $this->price;
}
public function setPrice($price){ // セッタ(価格設定)
$this->price = $price;
}
}
class BookProduct extends Product{
private $netprice = 15; // ネット価格
public function getNetprice(){ // ゲッタ(ネット価格取得)
return $this->netprice;
}
public function decrementNetprice(){//ネット価格を1円下げる
$this->netprice--;
return $this->getNetprice();
}
public function getPrice(){ // 親クラスゲッタ(価格取得)のオーバーライド=親クラスメソッドの上書き
$price = $this->price;
if ($this->netprice <= 10) { // ネット価格が10円以下になったら半額
$price = $price / 2;
}
return $price;
}
}
$book = new BookProduct();
$book->setPrice(100);// 価格を100に設定
while($book->getNetprice()>0){// ネット価格が0円になるまで繰り返す
echo 'ネット価格:' . $book->decrementNetprice() . '価格:'. $book->getPrice(). '<br />' ;
}
// 処理結果
// ネット価格:14価格:100
// ネット価格:13価格:100
// ネット価格:12価格:100
// ネット価格:11価格:100
// ネット価格:10価格:50
// ネット価格:9価格:50
// ネット価格:8価格:50
// ネット価格:7価格:50
// ネット価格:6価格:50
// ネット価格:5価格:50
// ネット価格:4価格:50
// ネット価格:3価格:50
// ネット価格:2価格:50
// ネット価格:1価格:50
// ネット価格:0価格:50
parent::
・メソッドをオーバーライドした後、オーバーライドする前(親クラスで定義された方)のメソッド利用を、子クラスで明確に指定
class Product{
protected $price;
protected $discountRate = 0.03;
public function getPrice(){ // 価格取得
$price = $this->price - ($this->price * $this->discountRate);
return $price;
}
public function setPrice($price){ // 価格設定
$this->price = $price;
}
}
class BookProduct extends Product{
private $netprice = 15; // ネット価格
public function getNetprice(){ // ゲッタ(ネット価格取得)
return $this->netprice;
}
public function decrementNetprice(){//ネット価格を1円下げる
$this->netprice--;
return $this->getNetprice();
}
public function getPrice(){ // 価格取得のオーバーライド
$price = parent::getPrice(); // 親クラスのgetPriceを呼び出し
if ($this->netprice <= 10) { // ネット価格が10円以下になったら半額
$price = $price / 2;
}
return $price;
}
}
$book = new BookProduct();
$book->setPrice(100);// 価格を100に設定
while($book->getNetprice()>0){// ネット価格が0円になるまで繰り返す
echo 'ネット価格:' . $book->decrementNetprice() . '価格:'. $book->getPrice(). '<br />' ;
}
// 処理結果
// ネット価格:14価格:97
// ネット価格:13価格:97
// ネット価格:12価格:97
// ネット価格:11価格:97
// ネット価格:10価格:48.5
// ネット価格:9価格:48.5
// ネット価格:8価格:48.5
// ネット価格:7価格:48.5
// ネット価格:6価格:48.5
// ネット価格:5価格:48.5
// ネット価格:4価格:48.5
// ネット価格:3価格:48.5
// ネット価格:2価格:48.5
// ネット価格:1価格:48.5
// ネット価格:0価格:48.5
オーバーライド後の、 メソッド内での「$this->メソッド();」の挙動
親クラスメソッド内
$this->メソッド()
・子クラスのメソッドが呼ばれる(実質的に「子クラス::メソッド()」)
※クラスを継承したため(=「子クラス」は、「親クラス(派生元のクラス)」が持つ全てのプロパティとメソッドを受け継いだため)
子クラスメソッド内
$this->メソッド()
・子クラスのメソッドが呼ばれる(実質的に「子クラス::メソッド()」)
parent::メソッド()
・親クラスのメソッドが呼ばれる
class AIRPOT{//基底クラス
function func(){//⑨メンバ関数funcが呼び出される
echo "[ 意味合い的には、AIRPOT::func() ]←⑨AIRPORT(基底)クラスのメンバ関数funcが呼び出される<br /> ";
echo '[ $this->func2() ]←⑩AIRPORT(基底)クラス内で、メンバ関数func2をコール<br />';
$this->func2();//←⑩メンバ関数func2をコール
echo '<br />⑫AIR(基底)クラス内における$thisを、「var_dump($this);」してみる。$thisとは、';
var_dump($this);//⑫
}
function func2(){
echo "I'm [ AIRPOT::func2() ]<br />←呼ばれない!!";
}
}
class STATION extends AIRPOT{//派生クラスSTATION
function STATION(){//②コンストラクタ実行
echo '[ $this->func() ]←③STATION(派生)クラス内で、メンバ関数funcをコール<br />';
$this->func();//③メンバ関数funcをコール
echo "<hr />";
echo "[ parent::func() ]←⑧STATION(派生)クラス内で、基底クラスで定義したメンバ関数funcをコール<br />";
parent::func();//⑧基底クラスで定義したメンバ関数funcをコール
}
function func(){//④メンバ関数funcが呼び出される
echo "[ 意味合い的には、STATION::func() ]←④STATION(派生)クラスのメンバ関数funcが呼び出される<br /> ";
echo '[ $this->func2() ]←⑤STATION(派生)クラス内で、メンバ関数func2をコール<br />';
$this->func2();//←⑤メンバ関数func2をコール
echo '<br />⑦STATION(派生)クラス内における$thisを、「var_dump($this);」してみる。$thisとは、';
var_dump($this);//⑦
}
function func2(){//⑥メンバ関数func2が呼び出される
echo "[ 意味合い的には、STATION::func2() ]←⑥⑪STATION(派生)クラスのメンバ関数func2が呼び出される<br />";
}
}
new STATION;//①コンストラクタの動きを見るためだけに実行。左辺がないのは、インスタンス(オブジェクト)を変数に格納させるつもりがないだけ
// 処理結果
// [ $this->func() ]←③STATION(派生)クラス内で、メンバ関数funcをコール
// [ 意味合い的には、STATION::func() ]←④STATION(派生)クラスのメンバ関数funcが呼び出される
// [ $this->func2() ]←⑤STATION(派生)クラス内で、メンバ関数func2をコール
// [ 意味合い的には、STATION::func2() ]←⑥⑪STATION(派生)クラスのメンバ関数func2が呼び出される
// ⑦STATION(派生)クラス内における$thisを、「var_dump($this);」してみる。$thisとは、object(STATION)#1 (0) { }
// [ parent::func() ]←⑧STATION(派生)クラス内で、基底クラスで定義したメンバ関数funcをコール
// [ 意味合い的には、AIRPOT::func() ]←⑨AIRPORT(基底)クラスのメンバ関数funcが呼び出される
// [ $this->func2() ]←⑩AIRPORT(基底)クラス内で、メンバ関数func2をコール
// [ 意味合い的には、STATION::func2() ]←⑥⑪STATION(派生)クラスのメンバ関数func2が呼び出される
// ⑫AIR(基底)クラス内における$thisを、「var_dump($this);」してみる。$thisとは、object(STATION)#1 (0) { }
PHPにおけるメソッドのオーバーライドについて /「引数の数や型は、親クラスのメソッドと完全に一致していなければなりません。」とは具体的にどういう意味ですか?
PHPにおけるメソッドのオーバーライドについて
PHPにおけるメソッドのオーバーライドについて説明します。
PHPでは、クラスの継承を通じて、親クラスからメソッドを継承し、子クラスで再定義することができます。これをメソッドのオーバーライドと呼びます。
公式ドキュメントの記述にある「子クラスのシグネチャが親クラスのそれと互換性がなければいけません」という文言は、オーバーライドする際のルールを説明しています。
メソッドのシグネチャとは、メソッドの名前や引数の型や数、および戻り値の型などの情報のことを指します。オーバーライドする場合、子クラスで再定義するメソッドのシグネチャは、親クラスのメソッドと互換性がなければなりません。
具体的には以下のルールが適用されます:
1.メソッド名は親クラスと同じでなければなりません。
2.引数の数や型は、親クラスのメソッドと完全に一致していなければなりません。
3.メソッドの可視性(public、protected、private)は、親クラスと同じか、より広い範囲でなければなりません。
4.親クラスのメソッドが戻り値の型を指定している場合、子クラスのメソッドも同じ型か、その型を継承した型を指定しなければなりません。ただし、PHP 7.0以降では、子クラスでより広い型を指定することも可能です。
これらのルールに従って、子クラスでメソッドをオーバーライドすることで、親クラスのメソッドの振る舞いを変更することができます。
ただし、注意点として、オーバーライドを行う際は、互換性を保つように慎重に設計する必要があります。互換性のないオーバーライドを行うと、コードの動作が予期しない結果になる可能性があります。
PHPでは、クラスの継承を通じて、親クラスからメソッドを継承し、子クラスで再定義することができます。これをメソッドのオーバーライドと呼びます。
公式ドキュメントの記述にある「子クラスのシグネチャが親クラスのそれと互換性がなければいけません」という文言は、オーバーライドする際のルールを説明しています。
メソッドのシグネチャとは、メソッドの名前や引数の型や数、および戻り値の型などの情報のことを指します。オーバーライドする場合、子クラスで再定義するメソッドのシグネチャは、親クラスのメソッドと互換性がなければなりません。
具体的には以下のルールが適用されます:
1.メソッド名は親クラスと同じでなければなりません。
2.引数の数や型は、親クラスのメソッドと完全に一致していなければなりません。
3.メソッドの可視性(public、protected、private)は、親クラスと同じか、より広い範囲でなければなりません。
4.親クラスのメソッドが戻り値の型を指定している場合、子クラスのメソッドも同じ型か、その型を継承した型を指定しなければなりません。ただし、PHP 7.0以降では、子クラスでより広い型を指定することも可能です。
これらのルールに従って、子クラスでメソッドをオーバーライドすることで、親クラスのメソッドの振る舞いを変更することができます。
ただし、注意点として、オーバーライドを行う際は、互換性を保つように慎重に設計する必要があります。互換性のないオーバーライドを行うと、コードの動作が予期しない結果になる可能性があります。
「引数の数や型は、親クラスのメソッドと完全に一致していなければなりません。」とは具体的にどういう意味ですか?
「=0」を付けるケースなどについて説明してください。
「引数の数や型は、親クラスのメソッドと完全に一致していなければなりません」という文言は、オーバーライドする際に引数の数と型が親クラスのメソッドと同じである必要があることを指しています。以下に具体的な説明をします。
引数の数の一致:
親クラスのメソッドが引数を受け取る場合、子クラスでのオーバーライドでは、同じ数の引数を指定する必要があります。つまり、オーバーライドするメソッドの引数リストの長さは、親クラスのメソッドと一致しなければなりません。
例として、以下のような親クラスと子クラスがあるとします:
引数の数の一致:
親クラスのメソッドが引数を受け取る場合、子クラスでのオーバーライドでは、同じ数の引数を指定する必要があります。つまり、オーバーライドするメソッドの引数リストの長さは、親クラスのメソッドと一致しなければなりません。
例として、以下のような親クラスと子クラスがあるとします:
class ParentClass {
public function doSomething($arg1, $arg2) {
// メソッドの処理
}
}
class ChildClass extends ParentClass {
public function doSomething($arg1, $arg2, $arg3) {
// メソッドの処理
}
}
上記の例では、親クラスのdoSomethingメソッドは2つの引数を受け取りますが、子クラスのオーバーライドでは3つの引数を受け取っています。この場合、引数の数が一致していないため、オーバーライドは正しくありません。
引数の型の一致:
親クラスのメソッドが引数の型を指定している場合、子クラスのオーバーライドでは、同じ型を指定する必要があります。親クラスで引数の型を指定していない場合は、子クラスでも型の指定は必須ではありません。
例として、以下のような親クラスと子クラスがあるとします:
引数の型の一致:
親クラスのメソッドが引数の型を指定している場合、子クラスのオーバーライドでは、同じ型を指定する必要があります。親クラスで引数の型を指定していない場合は、子クラスでも型の指定は必須ではありません。
例として、以下のような親クラスと子クラスがあるとします:
class ParentClass {
public function doSomething($arg1, $arg2) {
// メソッドの処理
}
}
class ChildClass extends ParentClass {
public function doSomething(string $arg1, int $arg2) {
// メソッドの処理
}
}
上記の例では、親クラスのdoSomethingメソッドの引数は型の指定がありませんが、子クラスのオーバーライドでは$arg1をstring型、$arg2をint型として指定しています。この場合、引数の型が一致しているため、オーバーライドは正しいと言えます。
「=0」を付けるケースは、引数のデフォルト値を指定する場合に使用されます。親クラスのメソッドがデフォルト値を持つ引数を持っている場合、子クラスのオーバーライドでは同じデフォルト値を指定する必要があります。
例として、以下のような親クラスと子クラスがあるとします:
「=0」を付けるケースは、引数のデフォルト値を指定する場合に使用されます。親クラスのメソッドがデフォルト値を持つ引数を持っている場合、子クラスのオーバーライドでは同じデフォルト値を指定する必要があります。
例として、以下のような親クラスと子クラスがあるとします:
class ParentClass {
public function doSomething($arg1, $arg2 = 0) {
// メソッドの処理
}
}
class ChildClass extends ParentClass {
public function doSomething($arg1, $arg2 = 10) {
// メソッドの処理
}
}
上記の例では、親クラスのdoSomethingメソッドの第二引数$arg2にはデフォルト値が0と指定されています。子クラスのオーバーライドでは、第二引数のデフォルト値を10と指定しています。この場合、デフォルト値が一致しているため、オーバーライドは正しいと言えます。
しかし、注意点として、デフォルト値を持つ引数は末尾に配置する必要があります。つまり、以下のような定義は無効です:
しかし、注意点として、デフォルト値を持つ引数は末尾に配置する必要があります。つまり、以下のような定義は無効です:
// 間違った例
class ParentClass {
public function doSomething($arg1 = 0, $arg2) {
// メソッドの処理
}
}
このような定義はエラーになります。
以上が、引数の数や型に関するオーバーライドのルールと、デフォルト値の扱いについての説明です。正しいオーバーライドを行うためには、これらのルールに従う必要があります。
以上が、引数の数や型に関するオーバーライドのルールと、デフォルト値の扱いについての説明です。正しいオーバーライドを行うためには、これらのルールに従う必要があります。
オーバーライドしたメソッドの引数に「 = null」を付けるケースについて / 子クラスのオーバーライドで、省略可能な引数として追加する例
オーバーライドしたメソッドの引数に「 = null」を付けるケースについて
オーバーライドしたメソッドの引数に「= null」を付けるケースは、オプショナルな引数を指定する場合に使用されます。親クラスのメソッドには引数がありますが、子クラスでのオーバーライドでは、その引数が省略可能であることを示すために「= null」を付けます。
具体的な説明を以下に示します。
親クラスのメソッドに引数がある場合、子クラスのオーバーライドでは同じ引数を持つ必要があります。ただし、子クラスでのオーバーライドでは、その引数を省略して呼び出すこともできるようにする場合があります。このような場合、引数に「= null」と指定してデフォルト値をnullに設定します。
例として、以下のような親クラスと子クラスがあるとします:
具体的な説明を以下に示します。
親クラスのメソッドに引数がある場合、子クラスのオーバーライドでは同じ引数を持つ必要があります。ただし、子クラスでのオーバーライドでは、その引数を省略して呼び出すこともできるようにする場合があります。このような場合、引数に「= null」と指定してデフォルト値をnullに設定します。
例として、以下のような親クラスと子クラスがあるとします:
class ParentClass {
public function doSomething($arg1, $arg2) {
// メソッドの処理
}
}
class ChildClass extends ParentClass {
public function doSomething($arg1, $arg2 = null) {
// メソッドの処理
}
}
上記の例では、親クラスのdoSomethingメソッドは2つの引数を持っていますが、子クラスのオーバーライドでは第二引数のデフォルト値をnullに指定しています。これにより、子クラスでのメソッド呼び出し時に第二引数を省略できるようになります。
以下は使用例です:
以下は使用例です:
$parent = new ParentClass();
$parent->doSomething('arg1', 'arg2');
$child = new ChildClass();
$child->doSomething('arg1'); // 第二引数を省略
子クラスのdoSomethingメソッドでは、第二引数を省略した場合に自動的にnullが設定されます。
このように、「= null」を付けることでオーバーライドしたメソッドの引数をオプショナルにすることができます。ただし、デフォルト値を持つ引数は末尾に配置する必要があります。
このように、「= null」を付けることでオーバーライドしたメソッドの引数をオプショナルにすることができます。ただし、デフォルト値を持つ引数は末尾に配置する必要があります。
子クラスのオーバーライドで、省略可能な引数として追加する例
下記は正しくオーバーライドできていますか
class ParentClass {
public function doSomething() {
// メソッドの処理
}
}
class ChildClass extends ParentClass {
public function doSomething($arg1 = null) {
// メソッドの処理
}
}
上記のコードは、正しくオーバーライドされています。
親クラスのdoSomethingメソッドは引数を持っていませんが、子クラスのオーバーライドでは$arg1を省略可能な引数として追加しています。これにより、子クラスでのメソッド呼び出し時に引数を省略することができます。
以下は使用例です:
親クラスのdoSomethingメソッドは引数を持っていませんが、子クラスのオーバーライドでは$arg1を省略可能な引数として追加しています。これにより、子クラスでのメソッド呼び出し時に引数を省略することができます。
以下は使用例です:
$parent = new ParentClass();
$parent->doSomething(); // 引数なしで呼び出し
$child = new ChildClass();
$child->doSomething(); // 引数なしで呼び出し
$child->doSomething('arg1'); // 引数を指定して呼び出し
子クラスのdoSomethingメソッドでは、引数$arg1のデフォルト値をnullとして指定しています。そのため、引数を省略した場合には$arg1には自動的にnullが設定されます。
このように、親クラスのメソッドと同じメソッド名を持ちながら、引数を追加することでオーバーライドを行うことができます。
このように、親クラスのメソッドと同じメソッド名を持ちながら、引数を追加することでオーバーライドを行うことができます。