



Studia grazie alle numerose risorse presenti su Docsity
Guadagna punti aiutando altri studenti oppure acquistali con un piano Premium
Prepara i tuoi esami
Studia grazie alle numerose risorse presenti su Docsity
Prepara i tuoi esami con i documenti condivisi da studenti come te su Docsity
Trova i documenti specifici per gli esami della tua università
Preparati con lezioni e prove svolte basate sui programmi universitari!
Rispondi a reali domande d’esame e scopri la tua preparazione
Riassumi i tuoi documenti, fagli domande, convertili in quiz e mappe concettuali
Studia con prove svolte, tesine e consigli utili
Togliti ogni dubbio leggendo le risposte alle domande fatte da altri studenti come te
Esplora i documenti più scaricati per gli argomenti di studio più popolari
Ottieni i punti per scaricare
Guadagna punti aiutando altri studenti oppure acquistali con un piano Premium
Appunti del corso di ALGORITMI E PROGRAMMAZIONE del prof. Fabio Sartori. Overloading, cast e abstract
Tipologia: Appunti
1 / 7
Questa pagina non è visibile nell’anteprima
Non perderti parti importanti!




Il costruttore è un “metodo” particolare che serve a definire gli attributi di un particolare oggetto, dove ricordiamo un oggetto è un’istanza di una classe, un esemplare. Il costruttore non può essere considerato un metodo perché non ha tipo di ritorno, la sua unica funzione è quella di inizializzare gli attributi. Come detto, la classe raggruppa a fattor comune le caratteristiche di un insieme di oggetti e, viceversa, a partire dalla classe vengono definiti i singoli oggetti come istanze. Le caratteristiche specifiche di tali istanze verranno date attraverso i costruttori che vengono definiti all’interno della definizione di classe. Il costruttore viene invocato nell’istante in cui a run time creo l’oggetto attraverso l’operatore new. Le eventuali modifiche di un attributo vengono fatte con il setter, che viene richiamato in caso di necessità, non direttamente sugli attributi. !!! Stampe, messaggi… non competono al costruttore. Il costruttore ha senso solo nell’istante in cui l’oggetto viene creato. Il costruttore costruisce l’oggetto secondo quelle che sono le specifiche date dal dominio di riferimento; nel fare ciò il costruttore può accettare parametri. Nel modellare un dominio potrò avere, a seconda della complessità dell’insieme di oggetti che ho, più attributi per definirne le caratteristiche. Non è detto che tali caratteristiche siano note tutte allo stesso istante (ossia immediatamente alla creazione dell’oggetto stesso), potrebbero divenire note in seguito. Per questo diventa essenziale la possibilità di specificare più costruttori: consente di creare oggetti a differenti livelli di granularità a seconda delle conoscenze che io ho a un certo istante di evoluzione, è un vantaggio da un punto di vista semantico. Ricordiamo inoltre che c’è anche costruttore di default per dare valori standardizzati, ma deve essere la nostra ultima risorsa: è scorretto pensare che tutti gli oggetti in un dominio nascano con gli stessi valori standardizzati. Che rapporto c’è fra i costruttori creati? Costruendo più costruttori all’interno di una classe ho l’overloading: questo rappresenta il legame esistente fra un costruttore più specifico e uno meno specifico. Se ho un costruttore di default nella classe e uno con più parametri, devo richiamare il costruttore con più parametri anche se il costruttore di default viene specificato sempre con i valori di default. Non effettuando l’overloading ma ridefinendo sempre il codice del costruttore di default, devo riprodurre il codice: il codice funziona ma questo non è il metodo ottimale. Il rischio infatti è quello di riprodurre dei punti di errore. L’obiettivo della programmazione è ridurre i punti di errore: realizzando overloading riusciamo a ridurre i margini di errore; definiamo un unico punto in cui il codice viene espresso e le modifiche si ripercuotono in maniera automatica dove c’è bisogno e se correggo errore una volta è corretto ovunque. Anche se il costruttore che devo ridefinire è quello di default, va fatto l’overloading.
Tornando ora all’ereditarietà, quando faccio extends avrò un nuovo costruttore che va a definire attributi in più. Siamo obbligati a richiamare il costruttore della superclasse perché gli attributi ereditati saranno definiti in superclasse. Abbiamo dunque visto due parole chiave: THIS, usata per fare overloading di costruttori all’interno di una stessa classe, e SUPER, usata per richiamare il costruttore di una classe base in una sottoclasse. Entrambe le keyword devono rappresentare la prima istruzione del costruttore quando compaiono, quindi non possono essere utilizzate insieme. Ma posso usare super per estendere il costruttore più specifico e poi invocare this per fare overloading degli altri costruttori interni della classe. Si fa Overriding su costruttore più specifico e overloading su costruttori meno specifici. Se la superclasse ha costruttore di default, anche ridefinito, viene richiamato in maniera automatica nelle sottoclassi, anche se non lo invoco esplicitamente. Invece se la superclasse esporta unicamente costruttori con parametri sono obbligato a invocarlo esplicitamente. Ci sono tanti casi in cui potrei ottenere dei side effects: soprattutto quando ho dei costruttori di default che stampano dei messaggi. Dunque sui metodi uso super con dot notation, nei costruttori super() Si ricordi inoltre che con i metodi si fa al contrario che con i costruttori, si parte dal meno specifico e poi si va al più specifico. Punto chiave: il nostro obiettivo è quello di MODELLARE I PROBLEMI! ANCORA UN ESEMPIO Realizziamo il seguente diagramma di classi. La relazione è quella di is-a. Creiamo una classe in cui definiamo un array di 3 animali ed inseriamo un gatto, un cane ed una papera Per ciascuno di essi invochiamo rispettivamente: dormi, parla e mangia. public abstract class Animale{ private String nome; public void dormi(){ System. out .println("Animale dorme"); } public void mangia(){ System. out .println("Animale mangia"); } public abstract void parla(); System. out .println("Dipendeeee") } }
Ciò che verrà stampato a video sarà: Animale dorme Animale dorme Animale dorme Animale mangia Animale mangia Animale mangia Miaoooo Qua quaaaa Bau Bauuuu Se non avessimo definito il metodo parla a livello di animale, il codice non avrebbe funzionato. definendolo, quello che si osserva nella stampa però è che non viene invocato: la stampa dovrebbe essere dipende, invece nel diagramma ho il metodo parla definito nei singoli oggetti. Ciò in realtà è coerente con il nostro obiettivo: il mio intento è quello di definire come viene implementato il metodo a livello della singola classe.
Permette di convertire un reference ad un tipo inferiore in gerarchia di parentela. Posso assegnare un reference ad un altro reference sempre purchè gli assegnamenti avvengano in un’opportuna gerarchia di ereditarietà. Sintassi: NomeClasseFiglia ref2 = (NomeClasseFiglia) ref1; Esempio: Animale a = new Gatto(“Miguel”); ... Gatto g = (Gatto)a; g.faiLeFusa(); Se so che l’oggetto creato è di classe gatto, posso assegnarlo ad un gatto g e invocare su g il metodo. Meglio se si effettua il controllo prima if (a instanceof Gatto) { Gatto g = (Gatto)a; g.faiLeFusa(); } Non posso scrivere a = new Gatto(); perchè il reference statico rimane animale: è necessario il cast esplicito anche se a tutti gli effetti ho l’oggetto gatto. Il metodo invocato è quello di gatto, non di animale. CONCETTI AVANZATI: CLASSI ASTRATTE Una classe può essere astratta: questo significa che non corrisponde a un concetto concreto del dominio applicativo, ma che è fatta apposta per funzionare da superclasse Esempio: non esiste un veicolo, esiste un'auto, una moto, ... Il metodo parla non viene invocato se ho animali veri. Devo definire il metodo parla, sennò ho un errore. Quindi sospetto che la classe animale sia utila a livello di documentazione per rappresentare correttamente il dominio ma che questa non debba essere effettivamente istanziabile. L’idea è quella di avere la possibilità all'interno di un dominio di rappresentare certe classi come indispensabili per la rappresentazione di un dominio ma all’atto pratico rendere impossibile la loro creazione effettiva. Una classe astratta è una classe che non può essere istanziata e ha senso solo in quanto ne vengono create delle sottoclassi. Viene definita solo per avere la funzione di superclasse: portano a fattor comune quelle che sono le caratteristiche comuni. Una classe deve essere dichiarata astratta se definisce almeno un metodo astratto. Una classe può essere dichiarata astratta anche se non definisce metodi astratti. Una delle caratteristiche principali è quella che definisce le caratteristiche di un gruppo di oggetti in maniera astratta. (Il metodo parla, al posto di essere definito in modo poco specifico “dipende” viene definito in termini astratti. La classe animale definirà il comportamento degli animali che sarà astratto nel senso che ogni animale mangia e dorme, ma poichè il comportamento di mangiare e dormire è concreto, lo rappresento in modo concreto. Siccome il modo in cui gli animali si esprimono non è unico, lo devo
In questo caso Felino non ridefinisce i metodi e quindi per definizione rimane astratta. L’EREDITARIETÀ MULTIPLA? Sulle classi astratte, posso estendere sempre e comunque una e una sola classe astratta (solo un extends). Ho un concetto analogo a quello della classe astratta: le interface. Queste vengono implementate e ci permettono di simulare l’ereditarietà multipla. L’interface realizza quasi un’associazione del tipo “is-a” al test instanceof restituisce true o false Le interface contengono solo intestazioni di metodi e al più definizioni di costanti Sintassi: public interface NomeInterface { // definizione nomi metodi } public class Classe implements NomeInterface { // definizione del corpo dei metodi ereditati } Si usa implements perché i metodi vanno completamente implementati, non ho nulla di concreto al loro interno. Posso implementare quante interface voglio. Esempio famoso: Runnable. CONFRONTARE LO STATO DI 2 OGGETTI L’operatore == applicato a 2 reference permette di testare se essi referenziano lo stesso oggetto. Ma se volessi sapere se due oggetti si trovano nello stesso stato, come faccio? Effettuo una comparazione fra i valori degli attributi. Modalità classica: