PHP继承

时间:2018-11-15 15:14:58  来源:igfitidea点击:

在本教程中,我们将学习如何使用PHP继承技术来复用现有类的逻辑。同时还将学习重载方法以及final类final方法的概念。

在本教程中,复用重用是一个意思,都是指重复使用的意思。

PHP继承介绍

继承的概念很强大。它允许您创建一个复用现有类的属性和方法的新类。
从另一个类继承的类称为子类(也称为派生类)。子类继承的类称为父类(也称为超类或基类)。
除了从父类继承属性和方法外,子类还可以具有其他属性和方法。

如果您想创建几个类似的类,继承是非常有用的。将公共属性和方法放在父类中,将特定属性和方法放在子类中。
这有助于避免在许多类中实现相同属性和方法的重复代码。

PHP继承例子

在前面PHP对象和类的教程中,我们创建了一个银行账户类(BankAccount)。
我们可以通过创建其他银行账户类来扩展银行账户类,例如,储蓄账户类(SavingAccount)和支票账户类(CheckingAccount),
它们都重用了BankAccount类的属性和方法:

储蓄账户(SavingAccount):是一种定期的银行账户,每月可以获得利息。
支票账户(CheckingAccount):是一个普通的银行账户,没有利息,每月扣除费用。

类文件结构

我们通常将每个类的代码放在一个单独的PHP文件中,该文件的名称与类名相同。
所有类文件通常位于一个名为classes的文件夹中,如下所示:

源代码示例

要从类继承,可以使用extends关键字。下面的例子说明了如何从BankAccount类继承出SavingAccount类:

/**
 * Description of savingaccount
 *
 * @author Administrator
 */
class SavingAccount extends BankAccount{
 /**
 * 每月的利率
 * @var float
 */
 private $interestRate;
 
 /**
 *  使用账号,初始金额和利率对储蓄账户进行初始化
 * interest rate
 * @param string $accountNo
 * @param float $initialAmount
 * @param float $interestRate
 */
 public function __construct($accountNo,$initialAmount,$interestRate){
    parent::__construct($accountNo, $initialAmount);
    $this->interestRate = $interestRate;
 }
 
 /**
 *  将利息增加到余额
 */
 public function addInterest(){
    $amount = parent::getBalance() * $this->interestRate / 100;
    parent::deposit($amount);
 }
}

首先,要从BankAccount类继承,使用了extends关键字。 如下所示:

class SavingAccount extends BankAccount{
....
}

然后,在SavingAccount类的构造函数中,我们使用了以下语法调用BankAccount类的构造函数:

parent::__construct($accountNo, $initialAmount);

同时,我们还在SavingAccount类的构造函数中对利率进行了初始化。

接着 ,我们向SavingAccount类添加addInterest()方法。在此方法中,我们使用以下语法调用父类(BankAccount)的方法:

parent::getBalance();

parent::deposit($amount);

如您所见,SavingAccount类的addInterest()方法的逻辑重用了父类(BankAccount)中方法的一些逻辑。

让我们测试新的SavingAccount类。

<?php
require_once('classes/bankaccount.php');
require_once('classes/savingaccount.php');
 
$sa = new SavingAccount('123456', 1000, 2.5);
 
echo '银行账户 #' . $sa->getAccountNumber() . '<br/>';
echo '余额: $' . $sa->getBalance() . '<br/>';
 
echo '加息...' . '<br/>';
$sa->addInterest();
 
echo '余额: $' . $sa->getBalance();

我们可以对CheckingAccount类使用相同的方法。

<?php
 
class CheckingAccount extends BankAccount{
 
    /**
    * monthly fee
    * @var float
    */
    private $fee;
    
    /**
    * 使用账户号码,初始金额和月费初始化账户
    * @param string $accountNo
    * @param float $initialAmount
    * @param float $fee
    */
    public function __construct($accountNo,$initialAmount,$fee){
        parent::__construct($accountNo, $initialAmount);
        $this->$fee = $fee;
    }
    
    /**
    *  从余额中扣除月费
    */
    public function deductFee(){
        parent::withdraw($this->fee);
    }
}

重载方法

要使子类的方法的行为不同,需要通过在子类中创建相同的方法名来重写父类中的方法。
在子类中的新方法中,可以使用父类的方法中的逻辑,也可以拥有自己的逻辑。

让我们看一个重写方法的例子。

在BankAccount类中,我们可以添加__toString()魔术函数,以便可以使用echo()函数将银行帐户对象显示为字符串。

以下是函数实现:

<?php
class BankAccount{
 /**
 * 以字符串形式返回银行帐户对象
 */
 public function __toString(){
    return sprintf("账号 #: %s <br/> 余额:$%0.2f",
    $this->accountNumber, 
    $this->totalBalance);
 }
       //..  其他方法和属性
}

在SavingAccount子类中,我们可以使用了同一个名称的__toString()函数。SavingAccount类中的__toString()函数将重写BankAccount类中的__toString()函数。

<?php
 
class SavingAccount extends BankAccount{
    public function __toString(){
        $str = parent::__toString();
        $str .= sprintf("<br/>利率:%0.2f",$this->interestRate);
        return $str;
    }
}

请注意,我们通过在子类SavingAccount中调用了BankAccount类的__toString()函数来保留BankAccount类中的__toString()函数的逻辑:

$str = parent::__toString();

现在,如果我们调用SavingAccount对象的__toString()函数,它的逻辑将生效。

<?php
require_once('classes/bankaccount.php');
require_once('classes/savingaccount.php');
 
$sa = new SavingAccount('1234567', 300, 1.5);
 
echo $sa;

Final类和Final方法

Final类

Final类不能被任何其他类继承。若要生成不能被继承的类,请使用final关键字,如下所示:

final class MyClass{
 //...
}

如果有一个类想要继承自final类,PHP将发出致命错误。为什么需要阻止其他类从一个类中继承呢?主要有两个原因:

  • 安全性:恶意黑客用来攻击系统的常见技术之一是创建类的子类并用子类替换父类。子类类似于父类,但其行为不同,会对系统造成损害。
  • 设计:你可能认为你的类已经是“完美的”,它不应该有任何子类。

Final方法

类的Final方法不允许子类重写它。通过这样做,您可以确保类中的方法的行为是一致的。

可以使用final关键字把方法变成final:

class MyClass{
 public final function finalMethod(){
 //...
 }
}