PHP - 继承
继承是面向对象编程方法论的基本原则之一。继承是一种软件建模方法,它允许扩展现有 class 的功能来构建新 class,而不是从零开始构建。
PHP 在其对象模型中提供了实现继承的所有功能。在 PHP 软件开发中引入继承可以实现代码复用、消除冗余代码重复并实现逻辑组织。
PHP 中的 Class 继承
假设你需要设计一个新 class,其大部分功能已经在现有 class 中定义得很好。继承允许你扩展现有 class,添加或移除其特性并开发新 class。事实上,PHP 使用 "extends" 关键字来建立现有 class 和新 class 之间的继承关系。
class newclass extends oldclass {
...
...
}
PHP 中的继承类型
PHP 中有三种继承类型 −
单继承:一个子 class 从一个父 class 继承。
多级继承:一个 class 从其子 class 继承。
层次继承:多个 class 从同一个父 class 派生,从而形成层次继承。
Is-a 关系
当一个新 class(以下简称继承 class、子 class、子类等)与一个现有 class(以下简称基 class、超 class、父 class 等)具有 "IS A" 关系时,继承就派上用场了。
在 PHP 中,当通过扩展另一个 class 定义新 class 时,子 class 会继承父 class 的 public 和 protected 方法、属性和常量。你可以自由覆盖继承方法的实现,否则它将保留父 class 中定义的功能。
多级继承示例
下面的代码展示了一个多级继承的示例 −
<?php
class Animal {
public function sound() {
echo "Animals make sound\n";
}
}
class Dog extends Animal {
public function bark() {
echo "Dog barks\n";
}
}
class Puppy extends Dog {
public function weep() {
echo "Puppy weeps\n";
}
}
$myPuppy = new Puppy();
$myPuppy->sound();
$myPuppy->bark();
$myPuppy->weep();
?>
这将产生下面的 输出 −
Animals make sound Dog barks Puppy weeps
继承中的访问修饰符
下面的代码展示了如何使用访问修饰符,这些修饰符决定属性和方法是否可以在 class 外部或内部使用 −
<?php
class Animal {
public $name = "Lion";
protected $type = "Wild";
private $age = 5;
public function getAge() {
return $this->age;
}
}
class Cat extends Animal {
public function showType() {
return $this->type;
}
}
$myCat = new Cat();
echo $myCat->name."\n";
echo $myCat->showType()."\n";
echo $myCat->getAge()."\n";
?>
这将产生下面的 输出 −
Lion Wild 5
PHP 中方法覆盖的示例
请查看以下示例 −
<?php
class myclass {
public function hello() {
echo "Hello from the parent class" . PHP_EOL;
}
public function thanks() {
echo "Thank you from parent class" . PHP_EOL;
}
}
class newclass extends myclass {
public function thanks() {
echo "Thank you from the child class" . PHP_EOL;
}
}
# 父类的对象
$obj1 = new myclass;
$obj1->hello();
$obj1->thanks();
# 子类的对象
$obj2 = new newclass;
$obj2->hello();
$obj2->thanks();
?>
它将产生以下 output −
Hello from the parent class Thank you from parent class Hello from the parent class Thank you from the child class
如前所述,子类会继承父类的 public 和 protected 成员(属性和方法)。子类可以引入额外的属性或方法。
在以下示例中,我们使用 Book class 作为父类。这里,我们创建一个 ebook class,它扩展了 Book class。新类有一个额外的属性 - format(表示 ebook 的文件格式 - EPUB、PDF、MOBI 等)。ebook class 定义了两个新方法来初始化和输出 ebook 数据 - 分别是 getebook() 和 dispebook()。
继承和扩展功能的示例
继承示例的完整代码如下 −
<?php
class Book {
/* 成员变量 */
protected int $price;
protected string $title;
public function getbook(string $param1, int $param2) {
$this->title = $param1;
$this->price = $param2;
}
public function dispbook() {
echo "Title: $this->title Price: $this->price \n";
}
}
class ebook extends Book {
private string $format;
public function getebook(string $param1, int $param2, string $param3) {
$this->title = $param1;
$this->price = $param2;
$this->format = $param3;
}
public function dispebook() {
echo "Title: $this->title Price: $this->price\n";
echo "Format: $this->format \n";
}
}
$eb = new ebook;
$eb->getebook("PHP Fundamentals", 450, "EPUB");
$eb->dispebook();
?>
浏览器 output 如以下所示 −
Title: PHP Fundamentals Price: 450 Format: EPUB
如果你仔细查看 getebook() 函数,前两个赋值语句实际上就是 getbook() 函数的内容,该函数已被 ebook class 继承。因此,我们可以使用 parent 关键字和作用域解析运算符来调用它。
将 getebook() 函数代码更改为以下内容 −
public function getebook(string $param1, int $param2, string $param3) {
parent::getbook($param1, $param2);
$this->format = $param3;
}
类似地,dispebook() 函数中的第一个 echo 语句被替换为对父类中 dispbook() 函数的调用 −
public function dispebook() {
parent::dispbook();
echo "Format: $this->format<br/>";
}
继承中的构造函数
父类中的构造函数会被子类继承,但如果子类定义了自己的构造函数,则无法直接在子类中调用它。
为了运行父类构造函数,需要在子类构造函数中调用 parent::__construct()。
示例
查看以下示例 −
<?php
class myclass{
public function __construct(){
echo "这是父类构造函数". PHP_EOL;
}
}
class newclass extends myclass {
public function __construct(){
parent::__construct();
echo "这是子类析构函数" . PHP_EOL;
}
}
$obj = new newclass();
?>
它将产生以下 输出 −
This is parent constructor This is child class destructor
然而,如果子类没有构造函数,则它可以像普通类方法一样从父类继承(如果未声明为 private)。
示例
查看以下示例 −
<?php
class myclass{
public function __construct(){
echo "这是父类构造函数". PHP_EOL;
}
}
class newclass extends myclass{ }
$obj = new newclass();
?>
它将产生以下 输出 −
This is parent constructor
Final 关键字的使用
在以下示例中,我们使用了 final 关键字。如果不希望方法或类被继承或修改,则使用 final 关键字。
<?php
class Animal {
final public function sound() {
echo "Animals make sound";
}
}
class Dog extends Animal {
// 这将产生错误
// public function sound() {
// echo "Dog barks";
// }
}
$myDog = new Dog();
$myDog->sound();
?>
输出
以上代码的输出如下 −
Animals make sound
继承的限制
PHP 不允许通过扩展多个父类来开发一个类。你可以拥有 hierarchical inheritance,其中 class B 扩展 class A,class C 扩展 class B,依此类推。但 PHP 不支持 multiple inheritance,即 class C 试图同时扩展 class A 和 class B。不过,我们可以扩展一个类并实现一个或多个 interfaces。我们将在后续章节中学习接口。