Override dei metodi (a runtime)
Fa parte della sezione Programmazione orientata agli oggetti del percorso Java di Coddy. Lezione 29 di 87.
Mentre l'overloading dei metodi viene risolto in fase di compilazione, l'override dei metodi dimostra il polimorfismo a runtime. In questo caso, Java decide quale metodo eseguire mentre il programma è effettivamente in esecuzione, in base al tipo effettivo dell'oggetto anziché al tipo dichiarato della variabile.
Hai imparato l'override dei metodi nel capitolo Ereditarietà. Ciò che lo rende polimorfico è il modo in cui Java lo gestisce quando un riferimento alla classe genitore punta a un oggetto della classe figlia:
public class Animal {
public void speak() {
System.out.println("Some sound");
}
}
public class Dog extends Animal {
@Override
public void speak() {
System.out.println("Bark!");
}
}
public class Cat extends Animal {
@Override
public void speak() {
System.out.println("Meow!");
}
}Ora osserva cosa succede quando usiamo riferimenti di tipo genitore:
Animal myPet = new Dog();
myPet.speak(); // Output: Bark!
myPet = new Cat();
myPet.speak(); // Output: Meow!Anche se myPet è dichiarato come Animal, Java chiama il metodo sovrascritto in base al tipo effettivo dell’oggetto durante l’esecuzione. Questo si chiama invio dinamico dei metodi. La JVM considera ciò che l’oggetto è realmente, non ciò che indica la variabile.
Questo permette di usare schemi potenti, come elaborare oggetti diversi in modo uniforme:
Animal[] pets = {new Dog(), new Cat(), new Dog()};
for (Animal pet : pets) {
pet.speak(); // Ognuno chiama la propria versione
}Il polimorfismo a tempo di esecuzione ti permette di scrivere codice flessibile che funziona con i tipi genitore, utilizzando automaticamente il comportamento corretto dei tipi figli.
Sfida
FacileCostruiamo un sistema di notifiche che dimostra il polimorfismo a tempo di esecuzione in azione. Creerai una gerarchia di tipi di notifica in cui la stessa chiamata a un metodo produce output diversi in base al tipo effettivo dell'oggetto durante l'esecuzione.
Organizzerai il codice in quattro file:
Notification.java: crea la classe base condivisa da tutte le notifiche. Ogni notifica ha un camporecipient(String). Includi un costruttore per inizializzarlo, un metodo getter e un metodosend()che stampa:Sending notification to [recipient]EmailNotification.java: crea una classe che estende Notification. Le notifiche e-mail aggiungono un camposubject. Usasuperper inizializzare la classe genitore. Sovrascrivi il metodosend()in modo che stampi:Emailing [recipient]: [subject]SMSNotification.java: crea un'altra classe che estende Notification. Le notifiche SMS aggiungono un campophoneNumber. Sovrascrivisend()in modo che stampi:Texting [phoneNumber] for [recipient]Main.java: è qui che il polimorfismo a tempo di esecuzione dà il meglio di sé! Riceverai tre input: il nome di un destinatario, l'oggetto di un'e-mail e un numero di telefono. Crea un array di riferimentiNotificationcontenente tre oggetti in questo ordine: una Notification di base, una EmailNotification e una SMSNotification (tutte con lo stesso destinatario). Poi scorri l'array e chiamasend()su ogni elemento. Osserva come Java chiama automaticamente la versione sovrascritta corretta in base al tipo effettivo di ciascun oggetto!
Riceverai tre input: il nome del destinatario (String), l'oggetto dell'e-mail (String) e il numero di telefono (String).
L'output dovrebbe mostrare tre righe: ogni chiamata a send() produce un output diverso, anche se stai chiamando lo stesso metodo su riferimenti Notification. È il polimorfismo dinamico in azione!
Provalo tu
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Leggi gli input
String recipient = scanner.nextLine();
String subject = scanner.nextLine();
String phoneNumber = scanner.nextLine();
// TODO: Crea un array di riferimenti a Notification con 3 elementi
// L'array deve contenere (in questo ordine):
// 1. Un oggetto Notification di base
// 2. Un oggetto EmailNotification
// 3. Un oggetto SMSNotification
// Tutti con lo stesso destinatario
// TODO: Scorri l'array e chiama send() su ogni elemento
// Questo dimostra il polimorfismo a runtime!
}
}
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