Docsity
Docsity

Prepara i tuoi esami
Prepara i tuoi esami

Studia grazie alle numerose risorse presenti su Docsity


Ottieni i punti per scaricare
Ottieni i punti per scaricare

Guadagna punti aiutando altri studenti oppure acquistali con un piano Premium


Guide e consigli
Guide e consigli


Algoritmi e programmazione: overloading e costruttori, Appunti di Algoritmi E Programmazione Avanzata

Appunti del corso di ALGORITMI E PROGRAMMAZIONE del prof. Fabio Sartori. Overloading, costruttori

Tipologia: Appunti

2022/2023

In vendita dal 05/06/2023

rossimartaa
rossimartaa 🇮🇹

4.3

(18)

156 documenti

1 / 10

Toggle sidebar

Questa pagina non è visibile nell’anteprima

Non perderti parti importanti!

bg1
OVERLOADING
Immaginiamo di avere il metodo per calcolare l’area dei quadrilateri: ad ogni quadrilatero diverso
corrisponde una formula diversa. Sono tutte delle figure che più o meno corrispondono a un’idea
simile ma sono effettivamente forme diverse a cui corrispondono formule diverse.
Potremmo ingegnarci per costruire un metodo generale che vada bene per tutti, ma ciò risulterebbe
scomodo.
Sarebbe bello avere un modo per associare a ciascun quadrilatero la sua forma.
Esempio: potremmo avere un metodo area rombo” che sta nella classe rombo, un metodo per il
trapezio nella classe trapezioavrei tantissimi oggetti tutti scollegati tra loro.
Quale sarebbe quindi la cosa ideale? Avere un unico progenitore quadrilatero che cerca una
soluzione unica che vada bene per tutti a patto di costruire un metodo molto complicato che trascuri
quelle che sono le formule specifiche, o tentare di seguirle, creando metodi specifici che però
portano ad avere esplosione di contenuti (ogni metodo dovrà essere replicato sulla sua classe)? La
cosa migliore sarebbe una via di mezzo.
La prima soluzione di avere un metodo unico porterebbe ad avere una soluzione che non rispecchia
pienamente la realtà del dominio che stiamo rappresentando: saremmo a posto da un punto di vista
algoritmico, ma non dal punto di vista della modellazione (il nostro obiettivo quest’anno è modellare
un dominio, cogliendone gli aspetti di rappresentazione e significato).
D’altra parte non posso neanche pensare di avere una specializzazione stretta che non consideri il
fatto che tutte le figure che ho vengono da un progenitore comune, perchè perderei delle proprietà
fondamentali: rischierei di vedere tutte queste figure una slegata dall’alta, perdendo le relazioni che
esistono tra di esse.
Le relazioni che esistono tra le entità del dominio che rappresentiamo devono sempre essere
considerate!!!
Il modo migliore per affrontare un problema di questo tipo è avere la possibilità di definire un
metodo generale che ci permetta di calcolare l’area di un quadrilatero generale, e avere poi la
possibilità di ridefinire il metodo per ciascuna classe dei quadrilateri a seconda della figura e delle
sue caratteristiche.
Si parte quindi da un metodo comune, e una volta definita la figura particolare che si vuole
considerare, si avrà la possibilità di capire quale sia il metodo specifico migliore da usare.
Questo è il concetto di overloading, ossia ridefinizione di un metodo (all’interno della stessa classe di
oggetti; poi lo vedremo anche all’interno di una gerarchia di classi differenti)
pf3
pf4
pf5
pf8
pf9
pfa

Anteprima parziale del testo

Scarica Algoritmi e programmazione: overloading e costruttori e più Appunti in PDF di Algoritmi E Programmazione Avanzata solo su Docsity!

OVERLOADING

Immaginiamo di avere il metodo per calcolare l’area dei quadrilateri: ad ogni quadrilatero diverso corrisponde una formula diversa. Sono tutte delle figure che più o meno corrispondono a un’idea simile ma sono effettivamente forme diverse a cui corrispondono formule diverse. Potremmo ingegnarci per costruire un metodo generale che vada bene per tutti, ma ciò risulterebbe scomodo. → Sarebbe bello avere un modo per associare a ciascun quadrilatero la sua forma. Esempio : potremmo avere un metodo “area rombo” che sta nella classe rombo, un metodo per il trapezio nella classe trapezio… avrei tantissimi oggetti tutti scollegati tra loro. Quale sarebbe quindi la cosa ideale? Avere un unico progenitore quadrilatero che cerca una soluzione unica che vada bene per tutti a patto di costruire un metodo molto complicato che trascuri quelle che sono le formule specifiche, o tentare di seguirle, creando metodi specifici che però portano ad avere esplosione di contenuti (ogni metodo dovrà essere replicato sulla sua classe)? La cosa migliore sarebbe una via di mezzo. La prima soluzione di avere un metodo unico porterebbe ad avere una soluzione che non rispecchia pienamente la realtà del dominio che stiamo rappresentando: saremmo a posto da un punto di vista algoritmico, ma non dal punto di vista della modellazione (il nostro obiettivo quest’anno è modellare un dominio, cogliendone gli aspetti di rappresentazione e significato). D’altra parte non posso neanche pensare di avere una specializzazione stretta che non consideri il fatto che tutte le figure che ho vengono da un progenitore comune, perchè perderei delle proprietà fondamentali: rischierei di vedere tutte queste figure una slegata dall’alta, perdendo le relazioni che esistono tra di esse. Le relazioni che esistono tra le entità del dominio che rappresentiamo devono sempre essere considerate!!! Il modo migliore per affrontare un problema di questo tipo è avere la possibilità di definire un metodo generale che ci permetta di calcolare l’area di un quadrilatero generale, e avere poi la possibilità di ridefinire il metodo per ciascuna classe dei quadrilateri a seconda della figura e delle sue caratteristiche. Si parte quindi da un metodo comune, e una volta definita la figura particolare che si vuole considerare, si avrà la possibilità di capire quale sia il metodo specifico migliore da usare. Questo è il concetto di overloading , ossia ridefinizione di un metodo (all’interno della stessa classe di oggetti; poi lo vedremo anche all’interno di una gerarchia di classi differenti)

OVERLOADING

  • Definizione formale:
    • operare overloading di un metodo vuol dire assegnare lo stesso nome a due o più definizioni differenti all’interno della stessa classe
  • Come fa Java a sapere quale definizione deve eseguire?
    • Java determina la definizione corretta sulla base del numero e del tipo di argomenti
  • Quando e perché si fa overloading? Non è più semplice definire metodi con nomi diversi?
    • quando diversi metodi semanticamente fanno la stessa cosa ma con parametri in ingresso diversi. Non ha senso modificare il nome Non si cambia il nome del metodo; cambia l’implementazione (interfaccia privata) e magari parte dell’interfaccia pubblica, come i parametri. Chiamo il metodo con lo stesso nome perché richiama la stessa operazione, poi modifico a livello di interfaccia il numero e il tipo di parametri su cui lavoro e l’implementazione, cambiando il modo in cui l’operazione viene effettuata. Generalmente il tipo di ritorno non cambia. In conclusione l’overloading corrisponde a più definizioni della stessa operazione a livello semantico: cambio set di parametri in ingresso e dunque anche l’implementazione. ESEMPIO
  • La classe LineaTelefonica fornisce anche un metodo che permette di richiamare l’ultimo numero selezionato
    • la definizione va modificata inserendo fra gli attributi di istanza anche una stringa che memorizza l’ultimo numero composto public class LineaTelefonica { public String numero; public String prefissoInternazionale; public String prefissoNazionale; /*Nota: Affinche' il confronto abbia effetto occorre comparare il prefisso nazionale e internazionale Infatti dati 2 prefissi internazionali diversi è possibile che il prefisso nazionale sia uguale!!!! Precondizioni :
  1. gli attributi di istanza prefissoInternazionale e prefissoNazionale devono essere !inizializzati! e in maniera corretta. Valori validi per l'internazionale sono quelli che iniziano con 00 e sono di 4 cifre.
  2. Il numero con cui deve essere comparato deve essere espresso nella forma prefisso internazionale
  • prefisso nazionale + numero. Postcondizione : restituisce false se il parametro attuale (o argomento) è un numero urbano rispetto al numero che questa linea rappresenta. Esempi:

COSTRUTTORI

Fino ad ora abbiamo sempre prima creato un oggetto e poi dato valore agli attributi. Dal punto di vista formale però non è il procedimento corretto: abbiamo sempre un istante in cui l’oggetto rimane inconsistente con la realtà. Ad esempio se creiamo il nostro quadrilatero con i suoi 4 lati di tipo double, quelli rimarranno con i valori di default pari a 0.0 fino a che non gli assegniamo noi dei valori. C’è un modo per far sì che l’oggetto nasca già con i giusti valori degli attributi, conoscendoli, così da evitare questa inconsistenza tra creazione e chiamata dei metodi di incapsulamento? Il modo che abbiamo è l’utilizzo dei costruttori. In realtà li abbiamo già usati inconsciamente. Quando abbiamo costruito oggetti con l’istruzione new nomeClasse e poi le parentesi tonde, abbiamo invocato il costruttore di default, ed era questo il motivo per cui agli attributi venivano assegnati questi valori di default. Questo però non ci può bastare: se conosciamo già i valori con cui l'oggetto deve nascere, dobbiamo essere in grado di assegnarli all’oggetto già in fase di creazione. L’INIZIALIZZAZIONE DEGLI OGGETTI

  • Quando si crea un oggetto è buona norma inizializzare i valori degli attributi
  • Come detto, gli attributi vengono inizializzati a valori di default
  • Non è detto che tali valori siano effettivamente quelli corretti. Come faccio a modificarli?
    • utilizzo i metodi di incapsulamento!
  • È possibile però che le operazioni di inizializzazione dell’oggetto vadano oltre alla pura assegnazione di valori iniziali per gli attributi
    • se devo costruire un’interfaccia grafica, voglio che tutte le operazione di costruzione vengano fatte nel momento in cui istanzio l’oggetto I COSTRUTTORI
  • Un costruttore è uno speciale tipo di metodo destinato ad effettuare le inizializzazioni dell’oggetto
  • Fino ad ora si sono creati oggetti grazie alla seguente sintassi: Sintassi: new NomeClasse() Esempio: new Automobile()
  • Tale sintassi crea l’oggetto ed inizializza gli attributi di istanza a valori di default (che non necessariamente sono quelli desiderati!)
  • Usando i costruttori è possibile creare oggetti inizializzandoli a valori desiderati
  • Il costruttore è un metodo speciale che viene invocato nel momento in cui viene creato un oggetto (e solo in quel momento!) Idea di avere un metodo particolare che in fase di creazione sia invocato e inizializzi gli attributi dell’oggetto così da non lasciarlo inconsistente.

LA SINTASSI

  • Come un metodo, un costruttore esegue tutte le istruzioni specificate nella sua definizione
    • quando si scrive un costruttore, occorre però ricordare che il suo scopo è quello di eseguire le azioni necessarie all'inizializzazione dell’oggetto
    • Esempio : la classe LineaTelefonica possiede una serie di metodi che hanno come precondizione l’inizializzazione della linea stessa: sarebbe opportuno, al posto di invocare i 3 metodi di incapsulamento, inizializzare opportunamente l’oggetto linea con i valori per i 3 attributi Sintassi: public NomeClasse([lista parametri formali]) { //inizializzazioni } NomeClasse ref = new NomeClasse([argomenti]) Non ha un tipo di ritorno, nemmeno void: se avesse tipo di ritorno sarebbe un metodo, ma il costruttore non è un metodo! E’ un metodo speciale che deve solo inizializzare gli attributi dell’oggetto, è un errore far fare altro al costruttore (errore logico: si carica la fase di creazione di istruzioni inutili). I parametri formali tra parentesi possono non esserci: se non ci sono sono i parametri di default. Infatti i parametri che vengono dati al costruttore sono proprio i valori a cui saranno inizializzati gli attributi. CON LA “VECCHIA” MODALITÀ public class Automobile { private int velocitaCorrente; private String proprietario; public void setVelocita(int v) { velocitaCorrente = v; } public void setProprietario(String p) { proprietario = p; } ... } public class TestAuto { public static void main(String ar[]) { //Voglio creare l’auto di Paperino con velocità 100 Automobile a = new Automobile(); a.setVelocita(100); a.setProprietario(“Paperino”); } }

public void setVelocita(int v) { if(v>=0 && v <= 180) { velocitaCorrente = v; } else { velocitaCorrente = 0; } } public void setProprietario(String p) { proprietario = p; } ... } public class TestAuto { public static void main(String ar[]) { //Voglio creare l’auto di Paperino con velocità 100 Automobile a = new Automobile(100, “Paperino”); } } Se mettessi controllo in costruttore e non in set, potrei costruire con il metodo set un'automobile con una velocità che non rispetta i vincoli. I controlli vanno messi nel metodo set perchè è quello che varia il valore. POSSO ANCORA USARE L’ISTANZIAZIONE CLASSICA? Considerando l’esempio precedente, la seguente istruzione è lecita? Automobile a = new Automobile(); NO! Perchè non esiste un costruttore nella classe Automobile che non accetta in ingresso parametri (chiamato costruttore di default)! Perché prima era lecita? E perché potevo istanziare oggetti?

  • Se non viene definito un costruttore all’interno di una classe, ne viene assegnato uno di default (costruttore di default) che non accetta in ingresso parametri e la cui implementazione è più o meno la seguente: public class Automobile { ... public Automobile() { } } Il costruttore di default viene applicato se non ce ne è uno definito dall’utente: se ce ne è uno definito dall’utente prevale quello, a meno che l’utente non lo ridefinisca opportunamente → posso fare overloading anche su costruttore.

OVERLODARE I COSTRUTTORI

Posso avere più costruttori all’interno di una classe, se ha senso. In genere ha senso se non ho tutti i parametri in ingresso: non sempre ho all’inizio tutte le informazioni per la costruzione dell’oggetto, soprattutto se i parametri sono molti. Posso così iniziare a inizializzare un certo set di parametri e dare valori di default agli altri, che verranno poi definiti con incapsulamento quando saranno disponibili.

  • Sempre!
    • ovviamente dove ha senso
    • che significa rendere la classe più flessibile
  • Overlodare i costruttori vuol dire realizzare più costruttori che inizializzano l’oggetto variando la lista dei parametri formali
    • nei costruttori in cui non viene richiesto il valore di inizializzazione di uno più attributi, devono, nell’implementazione, comunque inizializzare tali attributi ESEMPIO: DEFINIZIONE CLASSE AUTOMOBILE public class Automobile { private int velocitaCorrente; private String proprietario; public Automobile(int velocitaC, String proprietario) { il primo costruttore inizializza tutti gli attributi: velovitaC e proprietario setVelocitaCorrente(velocitaC); this.proprietario = proprietario; } public Automobile(String proprietario) { lascio al valore di default il parametro di cui non ho il valore velocitaCorrente = 0; this.proprietario = proprietario; } public Automobile(int velocita) { setVelocitaCorrente(velocita); this.proprietario = null; } public Automobile() { costruttore di default: no parametri a disposizione. lo sto ridefinendo perchè avendo ridefinito tutti i costruttori era stato cancellato velocitaCorrente = 0; this.proprietario = null; } ... }

LA KEYWORD THIS

Per fare ciò, non invoco esplicitamente il costruttore con il nome (avrei errore di ambiguità), ma utilizzo la keyword this (deve essere la prima istruzione nella definizione del metodo; spesso è anche l’unica). Non this. come se fosse variabile ma this() a mo’ di metodo. public class Automobile { private int velocitaCorrente; private String proprietario; public Automobile(int velocitaC, String proprietario) { setVelocitaCorrente(velocitaC); this.proprietario = proprietario; } public Automobile(String proprietario) { this(0, proprietario); } public Automobile(int velocita) { this(velocita, null); } public Automobile() { this(0, null); } ... } I costruttori eseguiranno l’inizializzazione mentre i metodi le operazioni. Con l’overloading sui metodi ho il rischio di cambiare i reference su cui sto lavorando: devo stare attenta a quale sia il metodo da richiamare all’interno dell’altro metodo. Conviene quindi partire dai metodi meno specifici, con un set di parametri meno esteso e richiamarli all’interno di quelli più estesi (contrario di quanto si fa con costruttori). Con i metodi è quindi necessario andare in senso inverso rispetto ai costruttori.