Classes Seladas (Java 17+)
Parte da seção Programação Orientada a Objetos do Journey de Java da Coddy — lição 52 de 87.
No Java tradicional, quando você cria uma classe, qualquer outra classe pode estendê-la (a menos que você a marque como final). Mas e se você quiser permitir a herança, mas não de qualquer uma? O Java 17 introduziu sealed classes para oferecer controle preciso sobre quais classes podem estender a sua.
Uma classe selada declara explicitamente suas subclasses permitidas usando as palavras-chave sealed e permits:
sealed class Shape permits Circle, Rectangle, Triangle {
abstract double area();
}
final class Circle extends Shape {
double radius;
double area() { return Math.PI * radius * radius; }
}
final class Rectangle extends Shape {
double width, height;
double area() { return width * height; }
}
final class Triangle extends Shape {
double base, height;
double area() { return 0.5 * base * height; }
}Apenas Circle, Rectangle e Triangle podem estender Shape. Qualquer outra classe que tente estendê-la causará um erro de compilação.
Cada subclasse permitida deve usar um de três modificadores: final (sem extensão adicional), sealed (continua a cadeia de restrição) ou non-sealed (abre para extensão irrestrita):
sealed class Vehicle permits Car, Truck { }
final class Car extends Vehicle { } // Não pode ser estendida
non-sealed class Truck extends Vehicle { } // Qualquer um pode estender TruckClasses seladas são particularmente poderosas com correspondência de padrões em expressões switch, pois o compilador conhece todos os subtipos possíveis e pode verificar a exaustividade. Elas são ideais ao modelar um conjunto fixo de tipos em um domínio, como métodos de pagamento, tipos de resposta ou formas geométricas.
Desafio
FácilVamos construir um sistema de notificação que demonstra classes seladas (sealed classes) restringindo quais tipos de notificações podem existir em sua aplicação. Você criará uma hierarquia onde apenas tipos específicos de notificação são permitidos, e cada tipo lida com a entrega de forma diferente.
Você organizará seu código em quatro arquivos:
Notification.java: Crie uma classe abstrata selada (sealed abstract class) que sirva como base para todas as notificações. Ela deve permitir exatamente três subclasses:EmailNotification,SMSNotificationePushNotification. Sua classe selada precisa de um campo protegidomessage(String), um construtor para inicializá-lo, um getter para a mensagem e um método abstratodeliver()que retorna uma String descrevendo como a notificação é enviada.EmailNotification.java: Crie uma classefinalque estenda Notification. Uma EmailNotification possui um campo privado adicionalrecipient(String) para o endereço de e-mail. Seu construtor deve receber tanto a mensagem quanto o destinatário, chamando o construtor pai comsuper(). O métododeliver()deve retornar:"Sending email to " + recipient + ": " + messageSMSNotification.java: Crie uma classenon-sealedque estenda Notification—isso permite que outras classes a estendam se necessário no futuro. Uma SMSNotification possui um campo privado adicionalphoneNumber(String). Seu construtor recebe a mensagem e o número de telefone. O métododeliver()deve retornar:"Sending SMS to " + phoneNumber + ": " + messagePushNotification.java: Crie uma classefinalque estenda Notification. Uma PushNotification possui um campo privado adicionaldeviceId(String). Seu construtor recebe a mensagem e o ID do dispositivo. O métododeliver()deve retornar:"Sending push to device " + deviceId + ": " + messageMain.java: Reúna seu sistema de notificação! Você receberá quatro entradas: uma mensagem (String), um endereço de e-mail (String), um número de telefone (String) e um ID de dispositivo (String).Crie uma notificação de cada tipo usando a mesma mensagem, mas suas respectivas informações de contato. Armazene as três em um array do tipo
Notification[]para demonstrar o polimorfismo com classes seladas. Em seguida, itere pelo array e imprima o resultado da chamada dedeliver()em cada notificação.
Você receberá quatro entradas na ordem: mensagem, endereço de e-mail, número de telefone e ID do dispositivo.
Observe como a classe selada restringe a hierarquia—apenas as três classes permitidas podem estender Notification. Cada subclasse deve se declarar como final, sealed ou non-sealed. Isso lhe dá controle total sobre sua hierarquia de tipos, ao mesmo tempo em que permite o polimorfismo!
Folha de consulta
Uma sealed class (classe selada) restringe quais classes podem estendê-la usando as palavras-chave sealed e permits:
sealed class Shape permits Circle, Rectangle, Triangle {
abstract double area();
}Apenas as classes listadas em permits podem estender a classe selada. Qualquer outra classe que tente estendê-la causará um erro de compilação.
Cada subclasse permitida deve usar um de três modificadores:
final- impede extensões futurassealed- continua a cadeia de restrição com suas próprias subclasses permitidasnon-sealed- abre a hierarquia para extensão irrestrita
sealed class Vehicle permits Car, Truck { }
final class Car extends Vehicle { } // Não pode ser estendida
non-sealed class Truck extends Vehicle { } // Qualquer um pode estender TruckClasses seladas são úteis para modelar um conjunto fixo de tipos em um domínio, como métodos de pagamento, tipos de resposta ou formas geométricas. Elas funcionam bem com pattern matching em expressões switch, pois o compilador conhece todos os subtipos possíveis.
Experimente você mesmo
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Leia as quatro entradas
String message = scanner.nextLine();
String email = scanner.nextLine();
String phoneNumber = scanner.nextLine();
String deviceId = scanner.nextLine();
// TODO: Crie um de cada tipo de notificação usando a mesma message
// - EmailNotification com message e email
// - SMSNotification com message e phoneNumber
// - PushNotification com message e deviceId
// TODO: Armazene todas as três notificações em um array Notification[]
// Isso demonstra polimorfismo com sealed classes
// TODO: Itere pelo array e imprima o resultado de deliver()
// para cada notificação
}
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Fundamentos de POO
Arquivos ExternosIntrodução à POOClasses vs ObjetosA Palavra-chave thisMétodosCampos (Atributos)Método ConstrutorSobrecarga de ConstrutorRecapitulação - Calculadora Simples4Herança
Herança Básica (extends)A Palavra-chave superSobrescrita de Método (@Override)Encadeamento de ConstrutoresA Classe ObjectHerança Simples e MultinívelPor que não há Herança Múltipla de ClassesRecapitulação - Hierarquia de Funcionários7Métodos Especiais e a Classe Object
Método toString()equals() e hashCode()Método clone()compareTo() e ComparableInterface ComparatorRecapitulação - Ordenação Personalizada2Modificadores de Acesso e Encapsulamento
Visão Geral dos Níveis de AcessoMétodos Getter e SetterOcultação de InformaçõesA Palavra-chave finalRecapitulação - Gerenciador de Conta Bancária5Polimorfismo
Fundamentos de Sobrecarga de MétodosSobrescrita de Métodos (Tempo de Execução)Upcasting e DowncastingO Operador instanceofClasses e Métodos AbstratosRecapitulação - Calculadora de Formas8Conceitos Avançados de OOP
Composição vs HerançaAgregação vs ComposiçãoClasses Internas, Aninhadas e AnônimasEnums e Métodos de EnumRecords (Java 16+)Classes Seladas (Java 17+)11Padrões de Projeto Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonPadrão FactoryPadrão BuilderPadrão ObserverPadrão Strategy3Propriedades de Classe e Membros Estáticos
Variáveis de Instância vs EstáticasMétodos EstáticosBlocos EstáticosConstantes (static final)Recapitulação - Contador e Utilitário6Interfaces e Classes Abstratas
Introdução às InterfacesImplementando InterfacesImplementação de Múltiplas InterfacesDefault e Static em InterfacesClasses Abstratas vs InterfacesInterfaces FuncionaisRecapitulação - Sistema de Pagamento9Generics
Introdução a GenericsClasses GenéricasMétodos GenéricosParâmetros de Tipos DelimitadosWildcards (?, extends, super)Recapitulação - Container Genérico12Padrões de Projeto Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositePadrão Iterator