Menu
Coddy logo textTech

Classes Seladas (Java 17+)

Parte da seção Programação Orientada a Objetos do Journey de Java da Coddy. Lição 52 de 87.

Em 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 pessoa? O Java 17 introduziu as classes seladas 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 abstract 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 dos três modificadores: final (sem extensão adicional), sealed (continua a cadeia de restrições) ou non-sealed (permite 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 Truck

Classes 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.

challenge icon

Desafio

Fácil

Vamos criar um sistema de notificações que demonstra classes seladas restringindo quais tipos de notificações podem existir na sua aplicação. Você criará uma hierarquia na qual somente tipos específicos de notificações são permitidos, e cada tipo tratará a entrega de uma maneira diferente.

Você organizará seu código em quatro arquivos:

  • Notification.java: Crie uma classe abstrata selada que sirva como base para todas as notificações. Ela deve permitir exatamente três subclasses: EmailNotification, SMSNotification e PushNotification. Sua classe selada precisa de um campo protegido message (String), um construtor para inicializá-lo, um getter para a mensagem e um método abstrato deliver() que retorne uma String descrevendo como a notificação é enviada.
  • EmailNotification.java: Crie uma classe final que estenda Notification. Uma EmailNotification tem um campo privado adicional recipient (String) para o endereço de e-mail. Seu construtor deve receber tanto a mensagem quanto o destinatário, chamando o construtor da classe pai com super(). O método deliver() deve retornar: Sending email to [recipient]: [message]
  • SMSNotification.java: Crie uma classe non-sealed que estenda Notification. Isso permite que outras classes a estendam, se necessário, no futuro. Uma SMSNotification tem um campo privado adicional phoneNumber (String). Seu construtor recebe a mensagem e o número de telefone. O método deliver() deve retornar: Sending SMS to [phoneNumber]: [message]
  • PushNotification.java: Crie uma classe final que estenda Notification. Uma PushNotification tem um campo privado adicional deviceId (String). Seu construtor recebe a mensagem e o ID do dispositivo. O método deliver() deve retornar: Sending push to device [deviceId]: [message]
  • Main.java: Reúna seu sistema de notificações! 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, percorra o array e imprima o resultado da chamada de deliver() em cada notificação.

Você receberá quatro entradas, nesta ordem: mensagem, endereço de e-mail, número de telefone e ID de dispositivo.

Observe como a classe selada restringe a hierarquia: somente as três classes permitidas podem estender Notification. Cada subclasse deve se declarar como final, sealed ou non-sealed. Isso oferece controle completo sobre sua hierarquia de tipos, enquanto ainda permite o polimorfismo!

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 mensagem
        // - 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
        
    }
}
quiz iconTeste seus conhecimentos

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

Pratique por conta própria: Compilador de Java online