Intermediate Class - Ballet Intermediate Class - IFBC
Ballet Intermediate Class - IFBC

Intermediate class no Java: o que realmente importa na prática

Classes intermediárias existem quando você tem uma hierarquia de herança com três ou mais níveis. A classe do meio pega responsabilidades genéricas da superclasse e já prepara algum comportamento específico antes de passar para as subclasses mais especializadas. Não é um conceito misterioso. É só herança comum aplicada em camadas. Eu comecei a prestar atenção nisso quando precisei refatorar um sistema de relatórios internos. Tínhamos uma classe base abstrata chamada RelatorioBase, que definia métodos como abrirConexao(), processarDados() e gerarPDF(). Todas as subclasses concretas precisavam fazer exatamente a mesma coisa nos dois primeiros métodos. Repeti aquele código três vezes em três subclasses diferentes, e o projeto virou uma bagunça em duas semanas. Foi aí que criei uma classe intermediária RelatorioPadrao entre RelatorioBase e as subclasses concretas, centralizando os métodos em comum e reduzindo a duplicação por cerca de 70% no código legado.

Quando usar uma intermediate class no dia a dia

O uso mais direto é quando duas ou mais subclasses compartilham implementações idênticas ou quase idênticas de um ou mais métodos. Se você perceber esse padrão repetindo, provavelmente está na hora de inserir uma classe intermediária na hierarquia. A regra prática que eu sigo é simples: se o mesmo bloco de código aparece em três subclasses ou mais, mova ele para uma classe intermediária e deixe as filhas apenas sobrescreverem o que for realmente único. Em Java, isso funciona porque o mecanismo de dispatch de métodos respeita a ordem de herança. Se a subclasses filhas não sobrescreverem um método definido na classe intermediária, o código executado será o da classe intermediária, não o da superclasse original. Esse detalhe parece óbvio, mas gera confusão comum quando alguém herda de duas hierarquias diferentes ao mesmo tempo.

👉 Clique no botão abaixo para saber mais sobre o assunto!

Um problema que eu encontrei na prática aconteceu com um projeto de integração com API de pagamentos. Tínhamos PagamentoBase como superclasse abstrata, PagamentoCartao e PagamentoPIX como subclasses concretas. Criei uma classe intermediária PagamentoPadrao para abrigar a lógica de validação de CPF/CNPJ e formatação de data. Quando um colega adicionou uma nova subclass PagamentoBoleto sem herdar de PagamentoPadrao, a validação simplesmente não rodava mais. O sistema passou a processar pagamentos com dados inválidos silenciosamente. A correção foi obrigar todas as subclasses a herdar de PagamentoPadrao explicitamente e adicionar uma verificação estática no construtor da classe base que lançava exceção se o método de validação não estivesse presente na cadeia de herança. Essa situação ilustra um ponto que poucos mencionam em tutoriais: a classe intermediária funciona bem quando a hierarquia é rígida e controlada. Quando a hierarquia é aberta ou quando múltiplas equipes trabalham em subclasses separadas, a falta de enforced contract pode gerar bugs silenciosos. Uma alternativa viável nesse cenário é usar composição ao invés de herança, passando um objeto de estratégia de validação via construtor. Custa um pouco mais de código inicial, mas elimina o problema de contratos implícitos.

Outro aspecto que merece atenção é o acoplamento à implementação. Classes intermediárias costumam expor métodos protegidos que ficam acessíveis a todas as subclasses diretas e indiretas. Isso pode ser útil, mas também cria dependências difusas. Eu recomendo limitar métodos protegidos a apenas aqueles que realmente precisam ser sobrescritos. Métodos públicos ou privados são opções mais seguras quando não há necessidade de extensibilidade. Se o seu projeto exige múltiplas formas de herança simultânea, Java não permite herança múltipla de classes. Nesse caso, uma interface combinada com uma classe intermediária é a solução padrão. Você define uma interface com os contratos obrigatórios e uma classe intermediária com implementações comuns. As subclasses finais herdam a classe intermediária e implementam a interface. É mais trabalho no início, mas evita surpresas depois.

Não existe configuração especial para ativar ou desativar classes intermediárias em nenhum framework. Elas funcionam dentro das regras normais de herança da linguagem. O que muda é a disciplina de quem desenha a hierarquia. Se a hierarquia for planejada sem pensar nos pontos de extensão futuros, você vai acabar refatorando tudo mais tarde. Planejar com antecedência economiza horas de debugging, especialmente em sistemas legados onde o custo de troca é alto. Para quem está começando agora, o exercício mais simples é pegar uma hierarquia existente com dois ou mais níveis e identificar quais métodos se repetem. Anote cada repetição. Quando encontrar três ou mais iguais, mapeie para uma nova classe intermediária. Faça o teste de compilação e execute os testes unitários. Se tudo passar, a refatoração está válida. Se algum teste falhar, provavelmente você quebrou uma premissa de contrato entre as classes, e aí vale revisar a assinatura dos métodos antes de prosseguir.