Переопределение метода позволяет дочернему классу изменить реализацию метода, уже предоставленного его родительским классом.
Чтобы переопределить метод, необходимо создать этот метод в дочернем классе с тем же именем, параметрами и типом возвращаемого значения.
Метод в родительском классе называется переопределенным методом, а метод в дочернем классе известен как переопределяющий метод. Код в замещающем методе переопределяет (или заменяет) код в замещающем методе.
PHP будет решать, какой метод (переопределенный или переопределяющий метод) вызывать, в зависимости от объекта, используемого для вызова метода.
Давайте рассмотрим пример, чтобы лучше понять переопределение метода.
В следующем примере определяется класс Robot с одним общедоступным методом greet() и класс Android, наследующий класс Robot:
<?php
class Robot
{
public function greet()
{
return 'Hello!';
}
}
class Android extends Robot
{
}
Когда вы вызываете метод greet() через экземпляр Android, PHP вызывает метод greet() класса Robot:
<?php
$android = new Android();
echo $android->greet(); // Hello!
Это типичный сценарий наследования.
Иногда вам нужно полностью заменить поведение метода родительского класса новым. В этом случае вам нужно переопределить метод родительского класса.
Чтобы переопределить метод, вы снова переопределяете метод родительского класса в дочернем классе, но используете другую логику.
В следующем примере добавим метод greet() к классу Android, который возвращает другое приветственное сообщение:
<?php
class Robot
{
public function greet()
{
return 'Hello!';
}
}
class Android extends Robot
{
public function greet()
{
return 'Hi';
}
}
$robot = new Robot();
echo $robot->greet(); // Hello
$android = new Android();
echo $android->greet(); // Hi!
Следующая диаграмма классов иллюстрирует отношения между классами Robot и Android:
Когда вы переопределяете метод, у вас будет две версии одного и того же метода: одна в родительском классе, а другая в дочернем классе.
Если вы хотите вызвать метод родительского класса в методе дочернего класса, вы не можете использовать ключевое слово $this для этого:
<?php
class Android extends Robot
{
public function greet()
{
$greeting = parent::greet();
return $greeting . ' from Android.';
}
}
$this-> greet() будет вызывать себя бесконечно.
Чтобы вызвать метод greet() класса Robot, вам необходимо использовать parent (: :), как показано ниже:
<?php
class Android extends Robot
{
public function greet()
{
$greeting = parent::greet();
return $greeting . ' from Android.';
}
}
В этом примере метод greet() в классе Andoird вызывает метод greet() класса Robot. Он объединяет строку, возвращаемую методом greet() метода Robot, со строкой «from Android» и возвращает объединенную строку.
Предположим, вам нужно определить новый класс CheckingAccount, расширяющий класс BankAccount.
<?php
class BankAccount
{
private $balance;
public function __construct($amount)
{
$this->balance = $amount;
}
public function getBalance()
{
return $this->balance;
}
public function deposit($amount)
{
if ($amount > 0) {
$this->balance += $amount;
}
return $this;
}
public function withdraw($amount)
{
if ($amount > 0 && $amount <= $this->balance) {
$this->balance -= $amount;
return true;
}
return false;
}
}
Метод withdraw() проверяет, больше ли сумма снятия, чем ноль, но меньше или равна текущему балансу, прежде чем вычитать ее из баланса.
Определяем класс CheckingAccount, который наследует класс BankAccount. Класс CheckingAccount также имеет метод withdraw(), который переопределяет метод withdraw() класса BankAccount:
<?php
class CheckingAccount extends BankAccount
{
private $minBalance;
public function __construct($amount, $minBalance)
{
if ($amount > 0 && $amount >= $minBalance) {
parent::__construct($amount);
$this->minBalance = $minBalance;
} else {
throw new InvalidArgumentException('amount must be more than zero and higher than the minimum balance');
}
}
public function withdraw($amount)
{
$canWithdraw = $amount > 0 &&
$this->getBalance() - $amount > $this->minBalance;
if ($canWithdraw) {
parent::withdraw($amount);
return true;
}
return false;
}
}
Метод withdraw() в классе CheckingAccount проверяет сумму снятия с минимальным балансом перед его вычетом.
Следующая диаграмма классов иллюстрирует отношения между классами BankAccount и CheckingAccount:
Чтобы предотвратить переопределение метода в дочернем классе метода, можно добавить в префикс метода ключевое слово final:
public final function methodName()
{
//...
}
В следующем примере добавим метод id() в класс Robot:
class Robot
{
public function greet()
{
return 'Hello!';
}
final public function id()
{
return uniqid();
}
}
Если вы попытаетесь переопределить метод id() в классе Android, вы получите сообщение об ошибке. Например:
class Android extends Robot
{
public function greet()
{
$greeting = parent::greet();
return $greeting . ' from Andoid.';
}
public function id()
{
return uniqid('Android-');
}
}
Ошибка:
Cannot override final method Robot::id()