24  Herança

Reuso com especialização: quando usar e quando evitar.

DicaAbertura narrativa

Todo bom programador evolui mais rápido quando entende por que um tema importa antes de memorizar sintaxe. Neste capítulo, vamos conectar conceito, contexto e prática para transformar teoria em habilidade real.

DicaMapa mental do capítulo
  • Ideia central: o que este tema resolve em projetos Java.
  • Linguagem técnica: quais termos você precisa dominar.
  • Aplicação prática: como usar no código do dia a dia.
  • Armadilhas comuns: erros frequentes de iniciantes.
  • Critério de domínio: como saber se você aprendeu de verdade.

24.1 Estudo de caso guiado

Imagine uma pequena plataforma acadêmica para cadastro de alunos, notas e turmas. A cada aula, evoluímos essa plataforma com um novo recurso. Neste capítulo, o foco é aplicar o tema para deixar o sistema mais claro, seguro e fácil de manter.

24.2 Exemplo comentado em Java

class Veiculo {
  void mover() { System.out.println("Movendo"); }
}
class Carro extends Veiculo {
  @Override
  void mover() { System.out.println("Carro em movimento"); }
}

24.3 Fundamentos tecnicos de heranca

Em Java, heranca representa uma relacao de especializacao do tipo is-a (“eh um”). Isso significa que uma classe derivada herda estado e comportamento da classe base, mas tambem pode refinar comportamentos para atender regras mais especificas do dominio.

Quando voce usa extends, a subclasse passa a ter acesso aos membros public e protected da superclasse, respeitando encapsulamento. Membros private continuam existindo no objeto, mas so podem ser acessados indiretamente por metodos da propria superclasse.

Uma forma pratica de validar se a heranca faz sentido e testar a frase: “Todo X eh um Y”. Por exemplo, Carro eh um Veiculo, mas Motor nao eh um Veiculo; nesse caso, composicao geralmente descreve melhor a modelagem.

24.4 Construtores e palavra-chave super

Construtores nao sao herdados, mas a inicializacao da superclasse sempre acontece antes da inicializacao da subclasse. Por isso, o uso de super(...) e essencial quando a classe base exige dados obrigatorios.

class Pessoa {
  protected String nome;

  Pessoa(String nome) {
    this.nome = nome;
  }
}

class Aluno extends Pessoa {
  private String matricula;

  Aluno(String nome, String matricula) {
    super(nome); // inicializa a parte Pessoa
    this.matricula = matricula;
  }
}

Quando super(...) nao e chamado explicitamente, o compilador tenta inserir super() automaticamente. Se a superclasse nao tiver construtor sem parametros, o codigo nao compila.

24.5 Sobrescrita, polimorfismo e @Override

Sobrescrita (override) ocorre quando a subclasse redefine um metodo herdado com a mesma assinatura. Esse recurso permite comportamento polimorfico: uma referencia do tipo da superclasse pode executar a implementacao da subclasse em tempo de execucao.

class Funcionario {
  double calcularBonus() { return 500.0; }
}

class Gerente extends Funcionario {
  @Override
  double calcularBonus() { return 1200.0; }
}

O uso de @Override e uma boa pratica porque o compilador valida sua intencao. Se houver erro de assinatura, voce recebe aviso cedo e evita bugs silenciosos.

24.6 Heranca e classes abstratas

Quando uma classe representa um conceito generico demais para ser instanciado diretamente, use abstract. Ela pode definir metodos concretos e metodos abstratos que obrigam subclasses a fornecer implementacao.

abstract class Forma {
  abstract double area();

  void exibirTipo() {
    System.out.println("Forma geometrica");
  }
}

class Retangulo extends Forma {
  private double base;
  private double altura;

  Retangulo(double base, double altura) {
    this.base = base;
    this.altura = altura;
  }

  @Override
  double area() {
    return base * altura;
  }
}

Esse desenho melhora extensibilidade porque define um contrato comum e reduz duplicacao de regras compartilhadas.

24.7 Limites: final, acoplamento e profundidade de hierarquia

Metodos final nao podem ser sobrescritos. Classes final nao podem ser herdadas. Esse recurso e util para proteger invariantes importantes e evitar extensoes indevidas.

Tambem e importante evitar hierarquias profundas demais. Quanto maior a cadeia de heranca, maior tende a ser o acoplamento e mais dificil fica entender efeitos colaterais de uma mudanca. Em nivel introdutorio, prefira hierarquias curtas, nomes claros e regras bem localizadas.

24.8 Quando preferir composicao em vez de heranca

Heranca e poderosa, mas nao e a unica forma de reuso. Em muitos casos, composicao oferece mais flexibilidade e menor risco de acoplamento rigido.

Exemplo mental rapido:

  • Heranca: Carro extends Veiculo (relacao is-a).
  • Composicao: Carro tem um Motor (relacao has-a).

Se a relacao principal for “tem um”, composicao costuma ser a decisao mais saudavel para evolucao futura do sistema.

24.9 Diagnostico de aprendizado tecnico

Para verificar se voce domina heranca com criterio, teste sua capacidade de:

  1. Identificar se um problema pede heranca ou composicao.
  2. Projetar superclasse com responsabilidade enxuta e coesa.
  3. Usar super(...) corretamente em cenarios com construtores obrigatorios.
  4. Aplicar sobrescrita com @Override sem quebrar contrato de comportamento.
  5. Argumentar por que uma hierarquia precisa (ou nao) ser abstrata.

24.10 Erros clássicos e como evitar

  1. Copiar código sem entender a intenção de cada linha.
  2. Ignorar nomes claros para classes, métodos e variáveis.
  3. Pular testes curtos após cada pequena alteração.
  4. Tentar otimizar antes de ter uma versão correta.

24.11 Checklist de domínio

24.12 Trilha de prática (20-30 min)

  1. Reescreva o exemplo em um arquivo novo.
  2. Altere duas regras do problema e ajuste o código.
  3. Adicione uma validação extra.
  4. Execute e registre o resultado esperado.

24.13 Fechamento

Ao finalizar este capítulo, você não deve apenas reconhecer a sintaxe: deve conseguir tomar decisões melhores de implementação. Esse é o passo que diferencia leitura passiva de aprendizado de verdade.

NotaExpansão didática complementar

Neste capitulo, o estudo de heranca em Java se torna realmente valioso quando voce deixa de enxergar o conteudo como uma lista de regras isoladas e passa a observar como cada decisao tecnica influencia a qualidade do programa, a facilidade de manutencao e a capacidade de adaptar a solucao sem quebrar o que ja estava funcionando, especialmente em atividades progressivas que simulam situacoes de projeto real.

Para consolidar o aprendizado com profundidade, vale estruturar sua pratica em uma sequencia objetiva na qual voce revisa o conceito principal, implementa um exemplo pequeno e legivel e, logo em seguida, analisa de maneira critica se houve melhoria concreta em reuso orientado a dominio, super e construtores e limites da generalizacao, porque esse ciclo consciente transforma estudo passivo em desenvolvimento de criterio tecnico.

Quando esse processo se repete ao longo das semanas, voce comeca a perceber que sua evolucao nao depende de decorar respostas prontas, mas sim de interpretar problemas com mais maturidade, justificar escolhas com argumentos claros e construir solucoes cada vez mais consistentes, o que representa exatamente a transicao de iniciante para praticante autonomo dentro da trilha de Java.