Menu
Coddy logo textTech

Classi sealed (Java 17+)

Fa parte della sezione Programmazione orientata agli oggetti del percorso Java di Coddy. Lezione 52 di 87.

In Java tradizionale, quando crei una classe, qualsiasi altra classe può estenderla (a meno che tu non la contrassegni come final). Ma cosa succede se vuoi consentire l'ereditarietà, ma non da chiunque? Java 17 ha introdotto le classi sealed per darti un controllo preciso su quali classi possono estendere la tua.

Una classe sigillata dichiara esplicitamente le proprie sottoclassi consentite usando le parole chiave 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; }
}

Solo Circle, Rectangle e Triangle possono estendere Shape. Qualsiasi altra classe che tenti di estenderla causerà un errore di compilazione.

Ogni sottoclasse consentita deve usare uno di tre modificatori: final (nessuna ulteriore estensione), sealed (continua la catena di restrizioni) oppure non-sealed (consente estensioni senza restrizioni):

sealed class Vehicle permits Car, Truck { }

final class Car extends Vehicle { }           // Non può essere estesa
non-sealed class Truck extends Vehicle { }    // Chiunque può estendere Truck

Le classi sealed sono particolarmente potenti con il pattern matching nelle espressioni switch, poiché il compilatore conosce tutti i possibili sottotipi e può verificare che i casi siano esaustivi. Sono ideali per modellare un insieme fisso di tipi in un dominio, come metodi di pagamento, tipi di risposta o forme geometriche.

challenge icon

Sfida

Facile

Creiamo un sistema di notifiche che dimostri le classi sealed limitando i tipi di notifiche che possono esistere nella tua applicazione. Creerai una gerarchia in cui sono consentiti solo tipi specifici di notifiche e ogni tipo gestisce la consegna in modo diverso.

Organizzerai il codice in quattro file:

  • Notification.java: crea una classe astratta sealed che funge da base per tutte le notifiche. Deve consentire esattamente tre sottoclassi: EmailNotification, SMSNotification e PushNotification. La classe sealed deve avere un campo protected message (String), un costruttore per inizializzarlo, un getter per il messaggio e un metodo astratto deliver() che restituisce una String che descrive come viene inviata la notifica.
  • EmailNotification.java: crea una classe final che estende Notification. EmailNotification ha un campo private aggiuntivo recipient (String) per l'indirizzo email. Il suo costruttore deve accettare sia il messaggio sia il destinatario e chiamare il costruttore della classe padre con super(). Il metodo deliver() deve restituire: Sending email to [recipient]: [message]
  • SMSNotification.java: crea una classe non-sealed che estende Notification. Questo consente ad altre classi di estenderla, se necessario in futuro. SMSNotification ha un campo private aggiuntivo phoneNumber (String). Il suo costruttore accetta il messaggio e il numero di telefono. Il metodo deliver() deve restituire: Sending SMS to [phoneNumber]: [message]
  • PushNotification.java: crea una classe final che estende Notification. PushNotification ha un campo private aggiuntivo deviceId (String). Il suo costruttore accetta il messaggio e l'ID del dispositivo. Il metodo deliver() deve restituire: Sending push to device [deviceId]: [message]
  • Main.java: metti insieme il tuo sistema di notifiche! Riceverai quattro input: un messaggio (String), un indirizzo email (String), un numero di telefono (String) e un ID del dispositivo (String).

    Crea un oggetto per ciascun tipo di notifica usando lo stesso messaggio, ma le rispettive informazioni di contatto. Memorizzali tutti e tre in un array di tipo Notification[] per dimostrare il polimorfismo con le classi sealed. Quindi scorri l'array e stampa il risultato della chiamata a deliver() su ciascuna notifica.

Riceverai quattro input nell'ordine seguente: messaggio, indirizzo email, numero di telefono e ID del dispositivo.

Nota come la classe sealed limiti la gerarchia: solo le tre classi consentite possono estendere Notification. Ogni sottoclasse deve dichiararsi final, sealed o non-sealed. Questo ti offre il pieno controllo sulla gerarchia dei tipi, consentendo comunque il polimorfismo!

Provalo tu

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // Leggi i quattro input
        String message = scanner.nextLine();
        String email = scanner.nextLine();
        String phoneNumber = scanner.nextLine();
        String deviceId = scanner.nextLine();
        
        // TODO: Crea una notifica per ciascun tipo usando lo stesso message
        // - EmailNotification con message ed email
        // - SMSNotification con message e phoneNumber
        // - PushNotification con message e deviceId
        
        // TODO: Memorizza tutte e tre le notifiche in un array Notification[]
        // Questo dimostra il polimorfismo con le classi sealed
        
        // TODO: Scorri l'array e stampa il risultato di deliver()
        // per ciascuna notifica
        
    }
}
quiz iconMettiti alla prova

Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.

Tutte le lezioni di Programmazione orientata agli oggetti

Esercitati da solo: Compilatore Java online