Classes Seladas em Java: Controle de Herança e Segurança de Tipo
Introdução
O paradigma orientado a objetos oferece a herança como um dos principais mecanismos de reutilização e especialização de código. No entanto, o uso excessivo ou descontrolado da herança pode comprometer a integridade de um sistema, permitindo extensões indevidas e dificultando a manutenção. A partir do Java 17, a linguagem passou a incluir um novo recurso denominado sealed classes, ou classes seladas, que permite controlar de forma explícita quais classes podem herdar de uma classe ou implementar uma interface. Este artigo apresenta uma visão clara e acessível sobre esse recurso, demonstrando sua utilidade e integração com outras funcionalidades modernas da linguagem, como records e pattern matching.
Conceito de Classe Selada
Uma classe selada é uma estrutura que limita a herança a um conjunto pré-determinado de subclasses. Em outras palavras, ela define quem pode estendê-la, trazendo maior previsibilidade e segurança para o modelo de herança. Esse comportamento é estabelecido por meio da palavra-chave sealed e da cláusula permits, que lista explicitamente os tipos autorizados.
Diferentemente das classes comuns, onde qualquer outra classe pode herdar (desde que não sejam final), as classes seladas definem um contrato fechado de extensão. Assim, o desenvolvedor pode equilibrar entre a rigidez de uma classe final e a abertura completa de uma classe comum.
Sintaxe e Funcionamento
A declaração de uma classe selada segue uma estrutura simples:
public sealed class Forma permits Circulo, Retangulo, Triangulo {
// Código comum a todas as formas
}
Nesse exemplo, apenas Circulo, Retangulo e Triangulo podem herdar de Forma. Caso outra classe tente estender Forma sem estar listada em permits, o compilador emitirá um erro.
Cada subclasse, por sua vez, deve especificar sua natureza de herança, podendo ser:
- final: impede novas heranças.
- sealed: continua selada, mas define suas próprias subclasses.
- non-sealed: reabre a hierarquia, permitindo heranças livres novamente.
Integração com Records
As classes seladas ganham especial utilidade quando combinadas com record, um tipo introduzido no Java 16 que representa dados imutáveis com sintaxe reduzida. Essa combinação é bastante comum em modelagens que envolvem entidades fixas ou variantes conhecidas, como formas geométricas, estados de um sistema ou tipos de eventos.
public sealed interface Forma permits Circulo, Retangulo, Triangulo {
double area();
}
public record Circulo(double raio) implements Forma {
public double area() { return Math.PI * raio * raio; }
}
public record Retangulo(double largura, double altura) implements Forma {
public double area() { return largura * altura; }
}
public record Triangulo(double base, double altura) implements Forma {
public double area() { return (base * altura) / 2; }
}
Nesse exemplo, cada forma é um record que implementa a interface selada Forma. Como records são final por padrão, eles se integram naturalmente à filosofia das classes seladas, oferecendo uma hierarquia segura e imutável.
Uso com Pattern Matching
A partir do Java 21, o recurso de pattern matching foi estendido para o comando switch, permitindo verificar o tipo de um objeto de forma direta e concisa. Essa funcionalidade é especialmente poderosa quando aplicada sobre hierarquias seladas, já que o compilador conhece todos os tipos possíveis.
public class Main {
public static void main(String[] args) {
Forma f = new Circulo(5);
String descricao = switch (f) {
case Circulo c -> "Círculo de raio " + c.raio() + ", área = " + c.area();
case Retangulo r -> "Retângulo de " + r.largura() + "x" + r.altura() + ", área = " + r.area();
case Triangulo t -> "Triângulo de base " + t.base() + " e altura " + t.altura() + ", área = " + t.area();
};
System.out.println(descricao);
}
}
Neste código, o switch avalia a instância f e, com base no tipo concreto, executa o bloco correspondente. O compilador garante que todos os subtipos de Forma sejam tratados, eliminando a necessidade de um caso default.
Benefícios e Aplicações
O principal benefício das classes seladas é o controle de herança, que permite aos desenvolvedores definir hierarquias de forma previsível e segura. Além disso, há ganhos em legibilidade e manutenibilidade do código, já que todas as possibilidades de extensão estão declaradas explicitamente.
Em termos práticos, as classes seladas são ideais para:
- Representar modelos de domínio fixos, como tipos de mensagens, eventos ou estados.
- Definir APIs públicas mais seguras, evitando extensões indevidas por bibliotecas externas.
- Trabalhar com pattern matching de forma exaustiva e verificada em tempo de compilação.
Conclusão
As classes seladas representam uma evolução significativa na modelagem de hierarquias em Java. Elas oferecem um meio-termo entre classes totalmente abertas e completamente finais, equilibrando flexibilidade e segurança. Quando utilizadas em conjunto com records e pattern matching, tornam o código mais expressivo, previsível e menos sujeito a erros.
Com essa adição, o Java se aproxima de outras linguagens modernas que já adotavam conceitos semelhantes, como Kotlin e Scala, consolidando-se como uma linguagem madura e cada vez mais voltada à clareza e segurança do desenvolvedor.
Fontes:
- https://www.baeldung.com/java-sealed-classes-interfaces
- https://docs.oracle.com/en/java/javase/17/language/sealed-classes-and-interfaces.html
- https://medium.com/@jeffersonfabriciodev/o-impacto-de-records-e-sealed-classes-no-java-16-663bc49b0834
--------------
Se você chegou até aqui, fico muito feliz!
Espero que tenha curtido a leitura tanto quanto eu gostei de escrever.
Se foi o caso, não esqueça de deixar aquele comentário, sua opinião faz toda a diferença!
Um grande abraço e até o próximo artigo!



