Tem um cenário em Java 21 que confunde bastante quando a modelagem usa sealed: você cobre todos os subtipos aparentes no switch e, ainda assim, o compilador continua dizendo que a expressão não é exaustiva.
O ponto que mais confunde é simples: sealed restringe quem pode herdar, mas não transforma a classe em abstract automaticamente. Se você deixa um tipo intermediário concreto, o compilador ainda considera que ele pode existir em runtime. Logo, o switch deixa de ser exaustivo.
sealed class Evento permits Sucesso, Falha {}
sealed class Falha extends Evento permits FalhaValidacao, FalhaInfra {}
final class Sucesso extends Evento {}
final class FalhaValidacao extends Falha {}
final class FalhaInfra extends Falha {}
Se eu escrevo:
return switch (evento) {
case Sucesso s -> "ok";
case FalhaValidacao f -> "validacao";
case FalhaInfra f -> "infra";
};
parece que tudo foi coberto. Mas não foi. Como Evento e Falha continuam concretos, ainda seria possível ter uma instância de new Evento() ou new Falha(). O compilador está certo em reclamar.
abstract sealedsealed interfacedefault / case Evento e -> ... quando o domínio ainda não estiver fechadoQuando eu quero exaustividade forte, meu checklist fica assim:
switch está refletindo o domínio real ou só a hierarquia que eu imaginei?Achei um excelente lembrete de que exaustividade não depende apenas do switch; ela depende principalmente da modelagem do domínio. Se a árvore permite valores intermediários, o compilador vai cobrar.
Queria ouvir como vocês estão modelando isso em projetos com Java 21, pattern matching e hierarquias mais profundas.
Carregando comentários...