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 TruckLe 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.
Sfida
FacileCreiamo 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,SMSNotificationePushNotification. La classe sealed deve avere un campo protectedmessage(String), un costruttore per inizializzarlo, un getter per il messaggio e un metodo astrattodeliver()che restituisce una String che descrive come viene inviata la notifica.EmailNotification.java: crea una classefinalche estende Notification. EmailNotification ha un campo private aggiuntivorecipient(String) per l'indirizzo email. Il suo costruttore deve accettare sia il messaggio sia il destinatario e chiamare il costruttore della classe padre consuper(). Il metododeliver()deve restituire:Sending email to [recipient]: [message]SMSNotification.java: crea una classenon-sealedche estende Notification. Questo consente ad altre classi di estenderla, se necessario in futuro. SMSNotification ha un campo private aggiuntivophoneNumber(String). Il suo costruttore accetta il messaggio e il numero di telefono. Il metododeliver()deve restituire:Sending SMS to [phoneNumber]: [message]PushNotification.java: crea una classefinalche estende Notification. PushNotification ha un campo private aggiuntivodeviceId(String). Il suo costruttore accetta il messaggio e l'ID del dispositivo. Il metododeliver()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 adeliver()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
}
}
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
1Fondamenti della programmazione a oggetti
File esterniIntroduzione alla programmazione a oggettiClassi e oggettiLa parola chiave thisMetodiCampi (attributi)Metodo costruttoreSovraccarico dei costruttoriRipasso - Calcolatrice semplice4Ereditarietà
Ereditarietà di base (extends)La parola chiave superSovrascrittura dei metodi (@Override)Concatenamento dei costruttoriLa classe ObjectEreditarietà singola e multilivelloPerché non esiste l'ereditarietà multipla delle classiRiepilogo: gerarchia dei dipendenti7Metodi speciali e classe Object
Metodo toString()equals() e hashCode()Metodo clone()compareTo() e ComparableInterfaccia ComparatorRiepilogo - Ordinamento personalizzato2Modificatori di accesso e incapsulamento
Panoramica dei livelli di accessoMetodi getter e setterNascondimento delle informazioniLa parola chiave finalRiepilogo - Gestore di conti bancari5Polimorfismo
Nozioni di base sull'overloading dei metodiOverride dei metodi (a runtime)Upcasting e downcastingL'operatore instanceofClassi e metodi astrattiRiepilogo - Calcolatore di forme8Concetti avanzati di OOP
Composizione vs ereditarietàAggregazione vs composizioneClassi interne, annidate e anonimeEnum e metodi degli EnumRecord (Java 16+)Classi sealed (Java 17+)11Modelli di progettazione Parte 1
Introduzione ai modelli di progettazionePattern SingletonPattern FactoryPattern BuilderPattern ObserverPattern Strategy3Proprietà di classe e membri statici
Variabili di istanza e staticheMetodi staticiBlocchi staticiCostanti (static final)Riepilogo - Contatore e utility6Interfacce e classi astratte
Introduzione alle interfacceImplementazione delle interfacceImplementazione di più interfacceMetodi default e static nelle interfacceClassi astratte e interfacce a confrontoInterfacce funzionaliRiepilogo: sistema di pagamento9Tipi generici
Introduzione ai tipi genericiClassi genericheMetodi genericiParametri di tipo con limitiCaratteri jolly (?, extends, super)Riepilogo - Contenitore generico12Modelli di progettazione – Parte 2
Pattern CommandPattern AdapterPattern DecoratorPattern Template MethodPattern StatePattern CompositePattern IteratorEsercitati da solo: Compilatore Java online