Herança e polimorfismo

A herança é o cerne da Programação Orientada a Objetos — “um aluno é um tipo de usuário”, “um gato é um tipo de animal”. As classes-pai definem o comportamento comum, e as classes-filho o estendem e o especializam. O polimorfismo permite lidar com diferentes tipos de objetos por meio de uma interface unificada.

1. extends — Herança

PHP
<?php
// Parent class (base class)
class Animal {
    public function __construct(
        public string $name
    ) {}
    
    public function speak(): string {
        return "{$this->name} makes a sound";
    }
    
    public function sleep(): string {
        return "{$this->name} is sleeping";
    }
}

// Child class inherits from parent
class Dog extends Animal {
    // Override the parent method
    public function speak(): string {
        return "{$this->name}: Woof!";
    }
    
    // Add a new method
    public function fetch(): string {
        return "{$this->name} went to fetch the ball";
    }
}

class Cat extends Animal {
    public function speak(): string {
        return "{$this->name}: Meow~";
    }
}

$dog = new Dog("Buddy");
echo $dog->speak();   // Buddy: Woof! (overridden method)
echo $dog->sleep();   // Buddy is sleeping (inherited method)
echo $dog->fetch();   // Buddy went to fetch the ball (new method)

$cat = new Cat("Luna");
echo $cat->speak();   // Luna: Meow~ (overridden method)
echo $cat->sleep();   // Luna is sleeping (inherited method)
?>
💡 Dica: O PHP utiliza herança única — uma classe só pode extends uma classe pai. A herança múltipla é possibilitada por meio de traits e interfaces (que serão abordados nas próximas aulas).


2. pai — Chamada de métodos do pai

Quando uma classe filha sobrescreve um método, mas você ainda deseja manter a lógica original da classe pai, use parent:::

PHP
<?php
class Vehicle {
    public function __construct(
        public string $brand,
        public int $year
    ) {}
    
    public function getInfo(): string {
        return "{$this->year} {$this->brand}";
    }
}

class ElectricCar extends Vehicle {
    public function __construct(
        string $brand,
        int $year,
        public int $batteryRange  // Extra property for electric cars
    ) {
        // Call the parent constructor
        parent::__construct($brand, $year);
    }
    
    // Override and extend the parent method
    public function getInfo(): string {
        return parent::getInfo() . " Electric (range: {$this->batteryRange}km)";
    }
}

$car = new ElectricCar("Tesla", 2026, 600);
echo $car->getInfo();  // 2026 Tesla Electric (range: 600km)
?>

3. final — Impedir herança/sobrescrita

PHP
<?php
class Template {
    // Algorithm skeleton—child classes must not modify it
    final public function render(): string {
        $html = $this->header();
        $html .= $this->body();
        $html .= $this->footer();
        return $html;
    }
    
    protected function header(): string {
        return "<header>Default Header</header>";
    }
    
    protected function body(): string {
        return "<main>Default Content</main>";
    }
    
    protected function footer(): string {
        return "<footer>Default Footer</footer>";
    }
}

// ✅ Child classes can override header/body/footer
class BlogTemplate extends Template {
    protected function header(): string {
        return "<header>Blog Title</header>";
    }
    
    // ❌ But they can't override render()—it's protected by final
    // public function render(): string { ... }
}
?>

4. instanceof — Verificação de tipo

PHP
<?php
function makeSound(Animal $animal): string {
    if ($animal instanceof Dog) {
        return $animal->speak() . " (Good dog!)";
    } elseif ($animal instanceof Cat) {
        return $animal->speak() . " (So cute!)";
    }
    return $animal->speak();
}

echo makeSound(new Dog("Buddy"));  // Buddy: Woof! (Good dog!)
echo makeSound(new Cat("Luna"));   // Luna: Meow~ (So cute!)
?>

5. Classes abstratas

As classes abstratas não podem ser instanciadas diretamente — seu objetivo é definir “como uma subclasse deve ser”:

▶ Exemplo: Hierarquia de classes abstratas de formas

PHP
<?php
abstract class Shape {
    public function __construct(
        protected string $color = "Black"
    ) {}
    
    // Abstract methods: subclasses must implement these
    abstract public function area(): float;
    abstract public function perimeter(): float;
    
    // Regular method: subclasses can inherit or override
    public function describe(): string {
        return "A {$this->color} shape, area: " . round($this->area(), 2);
    }
}

class Circle extends Shape {
    public function __construct(
        private float $radius,
        string $color = "Black"
    ) {
        parent::__construct($color);
    }
    
    public function area(): float {
        return pi() * pow($this->radius, 2);
    }
    
    public function perimeter(): float {
        return 2 * pi() * $this->radius;
    }
}

class Rectangle extends Shape {
    public function __construct(
        private float $width,
        private float $height,
        string $color = "Black"
    ) {
        parent::__construct($color);
    }
    
    public function area(): float {
        return $this->width * $this->height;
    }
    
    public function perimeter(): float {
        return 2 * ($this->width + $this->height);
    }
}

// $s = new Shape("Red");  // ❌ Abstract classes can't be instantiated
$c = new Circle(5, "Red");
echo $c->describe();  // A Red shape, area: 78.54

$r = new Rectangle(4, 6, "Blue");
echo $r->describe();  // A Blue shape, area: 24
?>
▶ Experimente

6. Interfaces

Uma interface define um contrato sobre “o que você pode fazer”, sem definir “como você faz isso”:

PHP
<?php
interface Payable {
    public function getAmount(): float;
    public function pay(): string;
}

interface Refundable {
    public function refund(): string;
}

// A class can implement multiple interfaces
class CreditCard implements Payable, Refundable {
    public function __construct(
        private string $cardNumber,
        private float $amount
    ) {}
    
    public function getAmount(): float {
        return $this->amount;
    }
    
    public function pay(): string {
        return "Credit card {$this->cardNumber} paid {$this->amount}";
    }
    
    public function refund(): string {
        return "Refunded {$this->amount} to card {$this->cardNumber}";
    }
}

class Crypto implements Payable {
    public function __construct(
        private float $amount
    ) {}
    
    public function getAmount(): float {
        return $this->amount;
    }
    
    public function pay(): string {
        return "Crypto payment of {$this->amount}";
    }
    // Crypto doesn't support refunds—so it doesn't implement Refundable
}
?>

7. Polimorfismo na prática

A ideia central do polimorfismo: lidar com objetos de diferentes subclasses por meio de um tipo pai/interface unificado:

▶ Exemplo: Sistema de pagamento polimórfico

PHP
<?php
class PaymentProcessor {
    /**
     * Process payment with polymorphism—works regardless of payment method
     */
    public function process(Payable $payment): void {
        echo "[Payment] {$payment->pay()}<br>";
        
        // Some payment methods support refunds
        if ($payment instanceof Refundable) {
            echo "[Refund Support] {$payment->refund()}<br>";
        }
    }
    
    /**
     * Batch settlement—since all implement Payable, we can handle them uniformly
     */
    public function settle(array $payments): float {
        $total = 0;
        foreach ($payments as $payment) {
            $total += $payment->getAmount();
            echo $payment->pay() . "<br>";
        }
        echo "Total: {$total}<br>";
        return $total;
    }
}

$processor = new PaymentProcessor();

// Polymorphism—the caller doesn't know the specific payment method
$processor->settle([
    new CreditCard("1234-5678", 199.00),
    new Crypto(0.05),
    new CreditCard("8765-4321", 59.99),
]);
?>
▶ Experimente
Característica classe abstrata interface
Pode ter propriedades
Pode ter métodos implementados ❌ (No PHP 8+ pode ter métodos padrão)
Pode conter constantes
Herança múltipla ❌ (herança única) ✅ (múltiplas interfaces)
Usado para Relações “é um” Capacidades “pode fazer”
💡 Dica: Orientação para a seleção: use classes abstratas quando as subclasses tiverem uma forte conexão lógica (Shape→Circle). Use interfaces quando classes não relacionadas precisarem de uma capacidade compartilhada (CreditCard/Crypto→Payable).

❓ Perguntas Frequentes

P: Quando devo usar abstract class em vez de interface? R: Use uma classe abstrata quando as subclasses compartilharem propriedades ou lógica (por exemplo, Shape possui uma propriedade de cor). Use uma interface quando classes completamente diferentes precisarem de uma funcionalidade comum (pagamento, registro em log, serialização).

P: Por que o PHP não suporta herança múltipla? R: A herança múltipla causa o “problema do diamante” — quando duas classes pai têm métodos com o mesmo nome, a classe filha não sabe qual deles usar. O PHP substitui a herança múltipla por interfaces e traits.

P: Qual é o objetivo das classes e métodos final? R: Evitar que a lógica crítica seja modificada. Use final em código essencial, como cálculos de pagamento ou autenticação, para garantir que as subclasses não possam sobrescrevê-lo, assegurando assim um comportamento consistente.

📖 Resumo

📝 Exercícios

  1. Crie uma classe pai Vehicle (marca/ano/getInfo) e, em seguida, crie as classes filhas Car e Motorcycle. Sobrescreva getInfo() utilizando parent::getInfo() para preservar a saída da classe pai.
  2. Crie uma interface Logger (log(string $message): void) e, em seguida, implemente duas classes: FileLogger (grava em um arquivo) e DatabaseLogger (exibe na tela). Use o polimorfismo para lidar com o registro de logs de maneira uniforme.
  3. Crie uma classe abstrata Employee (nome/salário + método abstrato calculateBonus()) e, em seguida, implemente as classes derivadas Manager e Developer, cada uma com sua própria lógica de cálculo de bônus.
Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%