Especialização em Sistemas com Java 21 LTS • Módulo de Herança e Contratos


🗺️ Mapa Conceitual do Tópico

classDiagram
    class Colaborador {
        <<abstract>>
        -String matricula
        -String nome
        -BigDecimal salarioBase
        +Colaborador(matricula, nome, salarioBase)
        +calcularRemuneracaoTotal()* BigDecimal
        +descreverHolerite() String
        +getSalarioBase() BigDecimal
    }

    class EngenheiroSoftware {
        -int horasPlantao
        -BigDecimal valorHoraPlantao
        +calcularRemuneracaoTotal() BigDecimal
    }

    class DiretorExecutivo {
        -BigDecimal participacaoLucros
        +calcularRemuneracaoTotal() BigDecimal
    }

    class ConsultorEspecialista {
        -BigDecimal bonusDesempenho
        +calcularRemuneracaoTotal() BigDecimal
    }

    Colaborador <|-- EngenheiroSoftware : "extends"
    Colaborador <|-- DiretorExecutivo : "extends"
    Colaborador <|-- ConsultorEspecialista : "extends"

    note for Colaborador "Despacho Dinâmico (invokevirtual):
O tipo estático é Colaborador,
mas o método executado depende do
tipo dinâmico na Heap (vtable lookup)"

🏛️ 1. Herança e o Princípio de Substituição de Liskov (LSP)

Em Java, a herança é estabelecida através da palavra-chave extends, permitindo que uma subclasse herde atributos e métodos de uma única superclasse direta (herança simples de implementação). Todas as classes em Java herdam implicitamente de java.lang.Object.

O Princípio de Substituição de Liskov (SOLID - L):

“Se $S$ é um subtipo de $T$, então objetos do tipo $T$ devem poder ser substituídos por objetos do tipo $S$ sem quebrar a consistência ou corretude do sistema.”

Para cumprir o LSP em Java:

  1. Uma subclasse não pode impor pré-condições mais restritivas que a superclasse.
  2. Não pode enfraquecer pós-condições (ex.: retornar tipos incompatíveis ou lançar novas exceções checadas não declaradas).
  3. Deve preservar os invariantes estabelecidos pela superclasse.

O Modificador final em Classes e Métodos:

  • Classes final: Impedem qualquer tentativa de herança (ex: java.lang.String, java.lang.Integer). Garante imutabilidade e viabiliza agressivas otimizações de inlining pelo compilador JIT (C2).
  • Métodos final: Não podem ser sobrescritos pelas subclasses.

🎭 2. Polimorfismo e Despacho Dinâmico de Métodos (vtable)

O polimorfismo permite que referências de um tipo mais genérico manipulem instâncias de tipos mais especializados em tempo de execução:

Colaborador dev = new EngenheiroSoftware("ENG-01", "Lucas", new BigDecimal("8000.00"), 10);
dev.calcularRemuneracaoTotal(); // Executa o método de EngenheiroSoftware!

Como a JVM Executa o Despacho Dinâmico:

  1. Tipo Estático: É o tipo declarado da variável (Colaborador). É checado pelo compilador javac em tempo de compilação.
  2. Tipo Dinâmico: É a classe concreta do objeto alocado na Heap (EngenheiroSoftware).
  3. Instrução invokevirtual: No bytecode, a chamada a métodos não-privados e não-estáticos utiliza invokevirtual. A JVM consulta uma tabela interna chamada vtable (Virtual Method Table) associada ao ponteiro de classe do objeto (klass pointer), localizando o endereço exato da implementação da subclasse.

🧱 3. Classes Abstratas vs Interfaces

Uma classe anotada com abstract atua como uma fundação parcial para uma hierarquia:

  • Não Instanciável: A tentativa de executar new Colaborador(...) resulta em erro de compilação.
  • Compartilhamento de Estado: Ao contrário de interfaces clássicas, classes abstratas podem conter atributos de instância protegidos ou privados, construtores e blocos de inicialização.
  • Construtor Obrigatório: Construtores em classes abstratas são acionados exclusivamente a partir dos construtores das subclasses via instrução super(...).

📊 4. Matriz Comparativa: Concreta vs Abstrata vs Record

Aspecto Classe Concreta Classe Abstrata Record (Java 16+)
Instanciação Direta (new) Sim Não Sim
Pode Conter Métodos Abstratos Não Sim Não
Pode Ser Estendida (extends) Sim (a menos de final) Sim Não (é implicitamente final)
Pode Herdar de Outra Classe Sim (apenas uma) Sim (apenas uma) Não (já herda de java.lang.Record)
Atributos de Instância Mutáveis Sim Sim Não (todos os componentes são final)
Finalidade Arquitetural Entidade de domínio comum Esqueleto/Template com estado compartilhado DTO imutável e portador transparente de dados

💻 5. Código de Demonstração Corporativo (Java 21 LTS)

package com.empresa.heranca;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import java.util.Objects;

/**
 * Hierarquia polimórfica de remuneração corporativa com despacho dinâmico.
 */
public class SistemaFolhaPolimorfica {

    public static abstract class Colaborador {
        private final String matricula;
        private final String nome;
        private final BigDecimal salarioBase;

        public Colaborador(String matricula, String nome, BigDecimal salarioBase) {
            this.matricula = Objects.requireNonNull(matricula, "Matrícula obrigatória.");
            this.nome = Objects.requireNonNull(nome, "Nome obrigatório.");
            this.salarioBase = Objects.requireNonNull(salarioBase, "Salário base obrigatório.");
        }

        // Método Abstrato polimórfico obrigatório nas subclasses
        public abstract BigDecimal calcularRemuneracaoTotal();

        // Template Method concreto
        public String emitirExtrato() {
            return String.format("[%s] %-25s | Salário Base: R$ %9.2f | Remuneração Total: R$ %9.2f",
                    matricula, nome, salarioBase, calcularRemuneracaoTotal());
        }

        public String getMatricula() { return matricula; }
        public String getNome() { return nome; }
        public BigDecimal getSalarioBase() { return salarioBase; }
    }

    public static class EngenheiroSoftware extends Colaborador {
        private final int horasSobreaviso;
        private final BigDecimal valorHoraSobreaviso;

        public EngenheiroSoftware(String matricula, String nome, BigDecimal salarioBase, int horasSobreaviso) {
            super(matricula, nome, salarioBase);
            this.horasSobreaviso = horasSobreaviso;
            this.valorHoraSobreaviso = new BigDecimal("85.00");
        }

        @Override
        public BigDecimal calcularRemuneracaoTotal() {
            BigDecimal adicionalPlantao = valorHoraSobreaviso.multiply(new BigDecimal(horasSobreaviso));
            return getSalarioBase().add(adicionalPlantao).setScale(2, RoundingMode.HALF_UP);
        }
    }

    public static class DiretorExecutivo extends Colaborador {
        private final BigDecimal participacaoLucros;

        public DiretorExecutivo(String matricula, String nome, BigDecimal salarioBase, BigDecimal participacaoLucros) {
            super(matricula, nome, salarioBase);
            this.participacaoLucros = participacaoLucros;
        }

        @Override
        public BigDecimal calcularRemuneracaoTotal() {
            return getSalarioBase().add(participacaoLucros).setScale(2, RoundingMode.HALF_UP);
        }
    }

    public static void main(String[] args) {
        System.out.println("================================================================================");
        System.out.println("          PROCESSAMENTO POLIMÓRFICO DA FOLHA DE PAGAMENTO (JAVA 21)             ");
        System.out.println("================================================================================");

        List<Colaborador> equipe = List.of(
            new EngenheiroSoftware("ENG-101", "Ana Paula Ramos", new BigDecimal("9500.00"), 18),
            new EngenheiroSoftware("ENG-102", "Carlos Eduardo Lima", new BigDecimal("7200.00"), 0),
            new DiretorExecutivo("DIR-001", "Roberto Drummond", new BigDecimal("28000.00"), new BigDecimal("15000.00"))
        );

        BigDecimal custoTotalFolha = BigDecimal.ZERO;

        for (Colaborador c : equipe) {
            System.out.println(c.emitirExtrato()); // Despacho dinâmico via invokevirtual
            custoTotalFolha = custoTotalFolha.add(c.calcularRemuneracaoTotal());
        }

        System.out.println("--------------------------------------------------------------------------------");
        System.out.printf("CUSTO CONSOLIDADO TOTAL DA FOLHA : R$ %,10.2f%n", custoTotalFolha);
        System.out.println("================================================================================");
    }
}