はじめに
PHP8の上級試験では、クラス継承に関する「共変性・反変性」や「多重継承の可否」が頻出テーマです。特に「戻り値の型は狭くできる」「引数の型は広げられる」といったルールは混乱しやすいポイント。また、問題文の言い回しによって「多重継承できるのでは?」と誤解しやすいため、正しい仕様を整理しておきましょう。
目次
共変性(戻り値の型)
- 親クラスの戻り値より「狭い型」を子クラスで指定できる。
- 例)親
int|float
→ 子int
はOK。
class Hoge {
public function func(int $num): int|float {
return $num * M_PI;
}
}
class Foo extends Hoge {
public function func(int|float $num): int {
return intval($num * $num * M_PI);
}
}
var_dump((new Hoge())->func(10)); // float(31.4159...)
var_dump((new Foo())->func(11.22)); // int(395)
✅ 戻り値が「狭く(int)」、引数が「広く(int|float)」なっており、正しく動作。
反変性(引数の型)
- 親クラスの引数より「広い型」を子クラスで指定できる。
- 上記の例で
int
→int|float
となっているのが反変性。
多重継承はできない
PHPのクラスは 多重継承をサポートしていません。次のように書くと構文エラーになります。
class Hoge2 {}
class Foo2 {}
class Bar2 extends Hoge2, Foo2 {} // ❌ Parse error
「先に定義されたものが優先される」のはTraitの話
問題文で「先に定義されたほうが優先される」とあるのは クラスの多重継承ではなく、Traitの多重利用の仕様です。
trait T1 {
public function hello() { echo "T1"; }
}
trait T2 {
public function hello() { echo "T2"; }
}
class MyClass {
use T1, T2; // T2 が優先される
}
(new MyClass())->hello(); // "T2"
✅ この挙動が「菱形継承問題を避けるPHPの解決策」。クラスではなくTraitに関する話だと押さえておくことが重要です。
まとめ
- クラス継承では「戻り値は狭く(共変性)」「引数は広く(反変性)」できる。
- PHPのクラスは多重継承できず、構文エラーになる。
- 「先に定義されたものが優先される」のは Traitの仕様。
👉 この区別を押さえておくと、試験のひっかけ問題に惑わされずに済みます