Menu

Dziedziczenie w PHP: extends, parent:: i nadpisywanie

W PHP klasa dziedziczy po innej przez class Dog extends Animal: dostaje publiczne i chronione właściwości oraz metody rodzica, może je nadpisać i nadal wywołać oryginał przez parent::method(). Poznaj nadpisywanie, protected, final, instanceof i pułapkę z metodami prywatnymi.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

W PHP klasa dziedziczy po innej przez extends: class Dog extends Animal daje Dog każdą publiczną i chronioną właściwość oraz metodę Animal. Potomek może dodawać nowe metody, nadpisywać odziedziczone, deklarując metodę o tej samej nazwie, i wywoływać oryginał przez parent::method().

Dog nie ma konstruktora, więc używa konstruktora Animal, dlatego new Dog('Rex') działa. Klasa potomna może rozszerzać tylko jednego rodzica; PHP nie ma wielodziedziczenia klas.

Wywołanie metody rodzica przez parent::

Nadpisanie całkowicie zastępuje metodę rodzica. Aby ją rozszerzyć zamiast zastępować, wywołaj parent::method() z potomka i zbuduj coś na jej wyniku:

To samo działa dla konstruktorów przez parent::__construct(...), co opisuje strona o konstruktorze, łącznie z tym, co się dzieje, gdy o tym zapomnisz.

protected vs private w klasach potomnych

Element protected jest ukryty przed kodem zewnętrznym, ale widoczny dla każdej klasy w rodzinie. Element private jest widoczny tylko w klasie, która go deklaruje, więc klasa potomna w ogóle go nie widzi:

isset($this->pin) daje w potomku fałsz, bo potomek nie ma dostępu do prywatnego $pin rodzica, a własna metoda rodzica hasPin() nadal go czyta. Używaj protected dla tego, na czym podklasy mają budować, a private dla szczegółów, które chcesz móc swobodnie zmieniać.

Polimorfizm i instanceof

Kod napisany dla typu rodzica działa z każdym potomkiem. Każdy obiekt wykonuje własną wersję nadpisanej metody, więc jedna pętla obsługuje je wszystkie. instanceof sprawdza, czy obiekt jest danej klasy albo któregoś z jej potomków:

Gdy rodzic nigdy nie powinien być używany samodzielnie (zwykły Shape o polu 0 ma mało sensu), uczyń go klasą abstrakcyjną i zadeklaruj area() jako abstrakcyjną.

Zasady nadpisywania: sygnatury muszą pozostać zgodne

Nadpisująca metoda musi przyjmować co najmniej to, co przyjmował rodzic, i zwracać coś, czego mogą użyć wywołujący rodzica. Możesz dodać parametr opcjonalny, poszerzyć typ parametru albo zawęzić typ zwracany; nie możesz dodać wymaganego parametru ani uczynić metody publicznej prywatną.

Zmień string $sep = ',' na wymagane string $sep, a PHP odmówi skompilowania klasy z błędem krytycznym („Declaration of CsvExporter::export(array rows,stringrows, string sep): string must be compatible with Exporter::export(array $rows): string”).

Klasy i metody final

final zatrzymuje dziedziczenie w tym miejscu. Metody final nie można nadpisać, a final class nie można rozszerzyć. Oba przypadki są sprawdzane przy kompilacji pliku, więc skrypt w ogóle się nie uruchamia:

<?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

Dla klasy final komunikat to Class B cannot extend final class A. Oznacz metodę jako final, gdy rodzic polega na tym, że działa ona dokładnie w jeden sposób, na przykład metoda sprawdzająca uprawnienia przed zrobieniem czegokolwiek innego.

Pułapka z metodami prywatnymi

Metoda prywatna nie jest dziedziczona, więc metoda potomka o tej samej nazwie jej nie nadpisuje. Kod w rodzicu nadal wywołuje własną prywatną wersję, co zaskakuje osoby, które spodziewają się wykonania metody potomka:

greet() nadal wypisuje Hello: $this->word() w rodzicu wskazuje na prywatną metodę rodzica. Zmień private na protected w Greeter i uruchom ponownie; teraz metoda potomka jest prawdziwym nadpisaniem, a greet() mówi Bonjour.

Najczęściej zadawane pytania

Jak działa extends w PHP?

class Dog extends Animal czyni Dog potomkiem Animal. Obiekt Dog ma każdą publiczną i chronioną właściwość oraz metodę Animal, może dodać własne i zastąpić (nadpisać) odziedziczone metody, deklarując metodę o tej samej nazwie.

Jak wywołać metodę rodzica w PHP?

Użyj parent::methodName() w metodzie potomka, na przykład return parent::describe() . ' and more';. Dla konstruktora jest to parent::__construct(...).

Czy PHP obsługuje wielodziedziczenie?

Nie. Klasa może rozszerzać tylko jedną klasę rodzica. Aby współdzielić zachowanie z kilku źródeł, implementuj kilka interfejsów (class A implements B, C) i używaj traitów (use Loggable, Timestamps;).

Co oznacza final w PHP?

Metody final nie można nadpisać w klasie potomnej, a final class nie można w ogóle rozszerzyć. Złamanie którejkolwiek zasady to błąd krytyczny przy kompilacji klasy, na przykład Cannot override final method A::f().

Czy klasa potomna ma dostęp do prywatnej właściwości rodzica w PHP?

Nie. Element private należy tylko do klasy, która go deklaruje, więc w potomku $this->pin to niezdefiniowana właściwość (ostrzeżenie, a potem null), a metoda potomka o tej samej nazwie nie nadpisuje prywatnej. Zadeklaruj element jako protected, gdy potrzebują go klasy potomne.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ