Herança é um dos pilares da programação orientada a objetos (POO) que permite que uma classe (chamada classe filha ou subclasse) herde atributos e métodos de outra classe (classe pai ou superclasse). Em PHP, a herança é implementada com a palavra-chave extends. Isso promove reutilização de código, organização e facilita a manutenção.

Nesta aula, exploraremos como usar extends para criar subclasses, como invocar métodos da classe pai com parent, como sobrescrever comportamentos e, principalmente, quando faz sentido aplicar herança em seus projetos.

extends

A palavra-chave extends é usada na declaração de uma classe para indicar que ela herda de outra classe. A classe filha ganha acesso a todos os métodos e propriedades públicas e protegidas da classe pai (a menos que sejam redeclarados).

Exemplo básico:

<?php
class Animal {
    public $nome;

    public function __construct($nome) {
        $this->nome = $nome;
    }

    public function fazerSom() {
        return "Som genérico";
    }
}

class Cachorro extends Animal {
    public function fazerSom() {
        return "Au au";
    }
}

$dog = new Cachorro("Rex");
echo $dog->nome; // Rex
echo $dog->fazerSom(); // Au au
?>

No exemplo, Cachorro herda a propriedade $nome e o construtor de Animal. A herança permite que não precisemos redefinir o construtor em Cachorro (a menos que queiramos modificar).

parent

A palavra-chave parent é usada dentro de uma classe filha para referenciar a classe pai. É especialmente útil quando queremos chamar um método da classe pai que foi sobrescrito, ou acessar o construtor original.

Exemplo:

<?php
class Animal {
    protected $tipo;

    public function __construct($tipo) {
        $this->tipo = $tipo;
    }

    public function getInfo() {
        return "Tipo: " . $this->tipo;
    }
}

class Gato extends Animal {
    private $nome;

    public function __construct($nome, $tipo) {
        parent::__construct($tipo); // chama o construtor da classe pai
        $this->nome = $nome;
    }

    public function getInfo() {
        return parent::getInfo() . " - Nome: " . $this->nome;
    }
}

$gato = new Gato("Mimi", "Felino");
echo $gato->getInfo(); // Tipo: Felino - Nome: Mimi
?>

Usar parent:: garante que a lógica da classe pai não seja perdida, permitindo estendê-la.

Sobrescrita

Sobrescrita (override) ocorre quando uma classe filha redefine um método ou propriedade herdada da classe pai. Em PHP, para que um método possa ser sobrescrito, ele deve ser declarado como public ou protected na classe pai. Métodos final não podem ser sobrescritos.

Exemplo:

<?php
class Veiculo {
    public function mover() {
        return "Veículo se movendo";
    }
}

class Carro extends Veiculo {
    public function mover() {
        return "Carro acelerando";
    }
}

$carro = new Carro();
echo $carro->mover(); // Carro acelerando
?>

Note que a assinatura do método deve ser compatível (mesmo nome e, a partir do PHP 8, os tipos de parâmetros e retorno devem ser covariantes/contravariantes).

Quando usar herança

A herança deve ser usada quando existe uma relação "é um" clara entre a classe filha e a classe pai. Por exemplo, um Cachorro é um Animal. Evite usar herança apenas para reutilizar código, pois isso pode levar a hierarquias frágeis. Prefira composição quando a relação for "tem um" (ex.: um Carro tem um Motor).

Boas práticas:

  • Mantenha a hierarquia rasa (poucos níveis).
  • Use classes abstratas para forçar implementação de métodos.
  • Evite sobrescrita excessiva; se precisar modificar muito comportamento, talvez a herança não seja adequada.
  • Documente a intenção da herança.

Quando a herança é mal aplicada, o código se torna acoplado e difícil de modificar. Avalie sempre se a composição não seria mais flexível.

Referências

Exercícios

  1. Crie uma classe Funcionario com propriedades nome e salario, e um método calcularBonus() que retorna 10% do salário. Depois crie uma classe Gerente que herda de Funcionario e sobrescreve calcularBonus() para retornar 20% do salário. Instancie um gerente e exiba o bônus.

    ✓ Resposta:
    <?php
    class Funcionario {
        public $nome;
        public $salario;
    
        public function __construct($nome, $salario) {
            $this->nome = $nome;
            $this->salario = $salario;
        }
    
        public function calcularBonus() {
            return $this->salario * 0.10;
        }
    }
    
    class Gerente extends Funcionario {
        public function calcularBonus() {
            return $this->salario * 0.20;
        }
    }
    
    $gerente = new Gerente("Ana", 5000);
    echo "Bônus do gerente: R$ " . $gerente->calcularBonus();
    ?>
  2. Usando a classe Funcionario do exercício anterior, crie uma classe Desenvolvedor que herda de Funcionario e não sobrescreve o método calcularBonus(). Instancie um desenvolvedor e chame o método. Explique o resultado.

    ✓ Resposta:
    <?php
    class Desenvolvedor extends Funcionario {
        // herda tudo de Funcionario
    }
    
    $dev = new Desenvolvedor("Carlos", 4000);
    echo "Bônus do dev: R$ " . $dev->calcularBonus(); // 10% = 400
    ?>

    O método calcularBonus() não foi sobrescrito, então o comportamento da classe pai é usado, retornando 10% do salário.

  3. Crie uma classe ContaBancaria com propriedade saldo (protegida) e métodos depositar($valor) e sacar($valor). Crie uma classe ContaPoupanca que herda de ContaBancaria e sobrescreve sacar($valor) para cobrar uma taxa de 2% sobre o valor sacado. Use parent:: para reutilizar a lógica de saque.

    ✓ Resposta:
    <?php
    class ContaBancaria {
        protected $saldo = 0;
    
        public function depositar($valor) {
            $this->saldo += $valor;
        }
    
        public function sacar($valor) {
            if ($valor <= $this->saldo) {
                $this->saldo -= $valor;
                return true;
            }
            return false;
        }
    
        public function getSaldo() {
            return $this->saldo;
        }
    }
    
    class ContaPoupanca extends ContaBancaria {
        public function sacar($valor) {
            $taxa = $valor * 0.02;
            $total = $valor + $taxa;
            return parent::sacar($total);
        }
    }
    
    $cp = new ContaPoupanca();
    $cp->depositar(1000);
    $cp->sacar(100);
    echo "Saldo após saque: " . $cp->getSaldo(); // 1000 - (100 + 2) = 898
    ?>
  4. Explique por que o seguinte código gera erro e corrija-o: class A { private function metodo() {} } class B extends A { public function metodo() {} }

    ✓ Resposta:

    O erro ocorre porque métodos privados não são herdados. A classe filha não pode sobrescrever um método privado; ela está definindo um método novo com o mesmo nome, mas a visibilidade não pode ser aumentada (de private para public) em uma sobrescrita. Para corrigir, torne o método protected ou public na classe pai, ou remova a sobrescrita.

  5. Implemente uma classe Forma abstrata com um método abstrato calcularArea(). Crie classes Quadrado e Circulo que herdam de Forma e implementam o método. Use parent se necessário (não é obrigatório).

    ✓ Resposta:
    <?php
    abstract class Forma {
        abstract public function calcularArea();
    }
    
    class Quadrado extends Forma {
        private $lado;
    
        public function __construct($lado) {
            $this->lado = $lado;
        }
    
        public function calcularArea() {
            return $this->lado * $this->lado;
        }
    }
    
    class Circulo extends Forma {
        private $raio;
    
        public function __construct($raio) {
            $this->raio = $raio;
        }
    
        public function calcularArea() {
            return pi() * $this->raio * $this->raio;
        }
    }
    
    $quadrado = new Quadrado(5);
    echo "Área do quadrado: " . $quadrado->calcularArea();
    $circulo = new Circulo(3);
    echo "Área do círculo: " . $circulo->calcularArea();
    ?>