Menu

PHP 继承:extends、parent:: 和方法重写

在 PHP 中,一个类用 class Dog extends Animal 继承另一个类:它获得父类的 public 和 protected 属性与方法,可以重写它们,也仍然可以用 parent::method() 调用原来的方法。本文讲解方法重写、protected、final、instanceof 以及 private 方法的陷阱。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

在 PHP 中,一个类用 extends 继承另一个类:class Dog extends Animal 让 Dog 获得 Animal 的所有 public 和 protected 属性与方法。子类可以添加新方法,可以通过声明同名方法来重写继承来的方法,还可以用 parent::method() 调用原来的方法。

Dog 没有构造函数,所以它使用 Animal 的构造函数,这就是 new Dog('Rex') 能正常工作的原因。一个子类只能继承一个父类;PHP 的类不支持多继承。

用 parent:: 调用父类方法

重写会完全替换父类的方法。如果你想扩展它而不是替换它,就在子类中调用 parent::method(),在它的结果基础上继续处理:

构造函数同样可以用 parent::__construct(...),构造函数页面介绍了这种用法,以及忘记调用它时会发生什么。

子类中的 protected 和 private

protected 成员对外部代码隐藏,但对整个类家族中的每个类都可见。private 成员只在声明它的类中可见,所以子类完全看不到它:

在子类中 isset($this->pin) 为 false,因为子类无法访问父类的 private $pin,而父类自己的方法 hasPin() 仍然能读取它。子类需要在其基础上扩展的成员用 protected,希望日后可以自由修改的实现细节用 private。

多态和 instanceof

针对父类类型编写的代码适用于每一个子类。每个对象都会运行自己版本的重写方法,所以一个循环就能处理所有对象。instanceof 检查一个对象是否属于某个类或它的任意子类:

如果父类本身不应该被单独使用(面积为 0 的普通 Shape 没什么意义),就把它写成抽象类,并把 area() 声明为抽象方法。

重写规则:方法签名必须保持兼容

重写的方法接收的参数必须至少涵盖父类方法所接收的,返回的值也必须能被父类的调用方使用。你可以添加可选参数、放宽参数类型或收窄返回类型;但不能添加必需参数,也不能把 public 方法改成 private。

把 string $sep = ',' 改成必需的 string $sep,PHP 就会拒绝编译这个类,并报致命错误("Declaration of CsvExporter::export(array rows,stringrows, string sep): 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,例如一个在做任何事之前先检查权限的方法。

private 方法的陷阱

private 方法不会被继承,所以子类中的同名方法并不会重写它。父类中的代码会继续调用自己的 private 版本,这会让期望运行子类方法的人感到意外:

greet() 仍然输出 Hello:父类中的 $this->word() 解析到的是父类自己的 private 方法。把 Greeter 中的 private 改成 protected 再运行一次;现在子类的方法才是真正的重写,greet() 会输出 Bonjour。

常见问题

PHP 中的 extends 是怎么工作的?

class Dog extends Animal 让 Dog 成为 Animal 的子类。Dog 对象拥有 Animal 的所有 public 和 protected 属性与方法,可以添加自己的成员,也可以通过声明同名方法来替换(重写)继承来的方法。

PHP 中怎么调用父类的方法?

在子类的方法中使用 parent::methodName(),例如 return parent::describe() . ' and more';。构造函数则是 parent::__construct(...)。

PHP 支持多继承吗?

不支持。一个类只能继承一个父类。要从多个来源共享行为,可以实现多个接口(class A implements B, C)并使用 trait(use Loggable, Timestamps;)。

PHP 中的 final 是什么意思?

final 方法不能被子类重写,final class 则完全不能被继承。违反任何一条规则,都会在编译该类时产生致命错误,例如 Cannot override final method A::f()。

PHP 中子类能访问父类的 private 属性吗?

不能。private 成员只属于声明它的那个类,所以在子类中 $this->pin 是一个未定义的属性(先出现警告,然后得到 null),子类中的同名方法也不会重写父类的 private 方法。如果子类需要使用某个成员,就把它声明为 protected。

Coddy 编程语言插图

和 Coddy 一起学编程

开始