在 PHP 中,一个类用 extends 继承另一个类:class Dog extends Animal 让 Dog 获得 Animal 的所有 public 和 protected 属性与方法。子类可以添加新方法,可以通过声明同名方法来重写继承来的方法,还可以用 parent::method() 调用原来的方法。
<?php
class Animal
{
public function __construct(protected string $name) {}
public function speak(): string
{
return "{$this->name} makes a sound";
}
public function name(): string
{
return $this->name;
}
}
class Dog extends Animal
{
public function speak(): string
{
return "{$this->name} barks";
}
public function fetch(): string
{
return "{$this->name} brings the ball back";
}
}
$generic = new Animal('Some animal');
$rex = new Dog('Rex');
echo $generic->speak(), "\n";
echo $rex->speak(), "\n"; // overridden
echo $rex->name(), "\n"; // inherited
echo $rex->fetch(), "\n"; // added
Dog 没有构造函数,所以它使用 Animal 的构造函数,这就是 new Dog('Rex') 能正常工作的原因。一个子类只能继承一个父类;PHP 的类不支持多继承。
重写的方法接收的参数必须至少涵盖父类方法所接收的,返回的值也必须能被父类的调用方使用。你可以添加可选参数、放宽参数类型或收窄返回类型;但不能添加必需参数,也不能把 public 方法改成 private。
<?php
class Exporter
{
public function export(array $rows): string
{
return implode(';', array_map(fn ($r) => implode(',', $r), $rows));
}
}
class CsvExporter extends Exporter
{
// Adds an optional parameter: compatible
public function export(array $rows, string $sep = ','): string
{
return implode("\n", array_map(fn ($r) => implode($sep, $r), $rows));
}
}
$rows = [['id', 'name'], [1, 'Ada'], [2, 'Linus']];
echo (new Exporter())->export($rows), "\n\n";
echo (new CsvExporter())->export($rows), "\n\n";
echo (new CsvExporter())->export($rows, "\t"), "\n";
把 string $sep = ',' 改成必需的 string $sep,PHP 就会拒绝编译这个类,并报致命错误("Declaration of CsvExporter::export(array rows,stringsep): string must be compatible with Exporter::export(array $rows): string")。
final 类和 final 方法
final 让继承在这里停止。final 方法不能被重写,final class 不能被继承。这两条都在编译文件时检查,所以脚本根本不会开始运行:
<?php
class Payment
{
final public function charge(int $cents): string
{
return "charged $cents";
}
}
class FreePayment extends Payment
{
public function charge(int $cents): string
{
return 'free';
}
}
PHP Fatal error: Cannot override final method Payment::charge() in /home/index.php on line 12
对于 final 类,错误信息是 Class B cannot extend final class A。当父类依赖某个方法以唯一确定的方式运行时,就把它标记为 final,例如一个在做任何事之前先检查权限的方法。