






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 (2 anno) del CDL in matematica presso l'università di Milano Bicocca. Oggetto degli appunti è la programmazione a oggetti in Java.
Tipologia: Appunti
1 / 11
Questa pagina non è visibile nell’anteprima
Non perderti parti importanti!







Nella vita reale siamo abituati a osservare e descrivere oggetti a vari livelli di dettaglio: Dai da mangiare a Fido ; Porta a passeggio il cane ; Di che razza è quel cane? Bisogna amare gli animali ; Fido , cane , razza e animale sono denotazioni di oggetti a diverso livello di astrazione.
Fido è un esemplare di cane Astrazione “ instance_of ”. L’oggetto Cane modella le proprietà comuni di un insieme di oggetti: Attributi: nome, peso, colore; Come si comporta: abbaia, morde; Quali comandi può ricevere: chiama, seduto. Non è necessario descrivere separatamente due cani Fido e Pluto (istanze): sono classificati come cani Cane è quindi una classe. Che legame c’è tra la classe Cane e le sue istanze? Intuitivamente, Cane è un concetto più astratto di Fido e Pluto; il legame è nei due sensi. Classificazione : capire che Fido è un cane – definire la rappresentazione di un cane. Istanziazione : creare istanze (esemplari) di cane.
Il legame concettuale che esiste tra una classe e le sue istanze è un particolare esempio di astrazione. Un’ astrazione è un procedimento concettuale per mezzo del quale: Si definisce un concetto più generale (astratto) a partire da concetti più elementari (concreti). Rimuovendo dalla descrizione del concetto gli aspetti di dettaglio e particolari. Mettendo in evidenza gli aspetti più comuni e generali. Astrazioni strutturali : modellano oggetti e relazioni fra questi.
Lega istanze e classi La classe definisce le caratteristiche comuni degli oggetti di un insieme Ogni oggetto della classe possiede le proprietà definite dalla classe Esempio: Fido è un esemplare di cane.
Lega una classe genitore (superclasse) a una o più classi figlie (sottoclassi) che ne sono sottoinsieme. Ogni oggetto della sottoclasse appartiene anche alla sua superclasse Esempi: o I cani sono mammiferi (la classe Cane è una sottoclasse della classe Mammifero o Ogni esemplare di Cane possiede tutte le proprietà definire dalla classe Cane e anche tutte le proprietà definite dalla classe mammifero. Attenzione: non confondere classificazione e generalizzazione! o Fido è un esemplare di cane (instance_of) o Cane è un mammifero (is_a)
Lega una classe “Aggregato” con un insieme di classi “Parti”: ogni oggetto di Aggregato è costituito da oggetti delle classi Parti. Caso particolare di Associazioni. Esempio: un’automobile comprende motore, carrozzeria, ruote, … Attenzione: non confondere aggregazione e generalizzazione! o Un cane è un mammifero (is_a) o Un cane fa parte di un branco (part_of)
Collegano classi e sono il mezzo attraverso cui gli oggetti possono interagire. Un’associazione è un legame semantico fra classi; significa che fra i corrispondenti oggetti c’è un legame (detto link). Un link è un’istanza di associazione così come un oggetto è istanza di una classe. Per default, un’associazione è bidirezionale, anche se può essere resa unidirezionale. Esempi: Cane, Persona una Persona è padrona di un Cane. UML: diagramma delle classi Identificazione delle classi e delle relazioni fra le stesse: attività fondamentale in fase di analisi e di progetto.
Nome : esprime il significato dell’associazione (possiede): spesso è un verbo. Ruolo : esprime il ruolo giocato dal partner (cane-padrone): spesso è un sostantivo o un aggettivo. Cardinalità : esprime quante istanze della classe possono essere associate all’altra classe: o : molti o 1..: da 1 a molti o 1..5: da 1 a 5 o 0..1: nessuno o 1 o 1,4,6: o 1 o 4 o 6 Multiple : esplicita più associazioni tra una coppia di classi per arricchire la descrizione Navigabilità: esplicita chi vede cosa; come si vedrà determina la modalità con cui viene realizzata l’associazione.
Metodi Definire un metodo comporta la definizione di: Intestazione (o signature): ci permette di invocare successivamente il metodo; Body : definisce il comportamento del metodo. Contiene la sequenza di istruzioni che deve essere eseguita nel momento in cui il metodo viene invocato. Il body è racchiuso tra parentesi graffe e segue la definizione della signature o intestazione del metodo stesso. Esistono due tipi di metodi: Quelli che eseguono esclusivamente istruzioni (void). Non ho l’obbligo di inserire la keyword return. Quelli che eseguono istruzioni e restituiscono un valore (int, double, String, boolean,..). Nel body di questi metodi l’ultima istruzione che viene eseguita deve essere return valore. Tutti i metodi devono essere descritti. La descrizione dovrebbe contenere: Le precondizioni : indicano le condizioni che devono essere vere prima di invocare un metodo; il metodo non deve mai essere invocato se le precondizioni non sono verificate (potrebbe fornire risultati non attesi). Le postcondizioni : descrivono gli effetti prodotti dall’invocazione del metodo (ciò che viene restituito se il metodo restituisce un valore, gli effetti sullo stato del calling object se void). Incapsulamento L’incapsulamento permette di separare l’interfaccia dall’implementazione: L’ interfaccia contiene ciò che è visibile all’esterno (cioè a chi utilizza la classe incapsulata). L’ implementazione contiene tutti i dettagli implementativi che si vogliono nascondere a chi utilizza la classe incapsulata. Che vantaggi nell’effettuare l’incapsulamento? Riuso del codice: un programma che usa un metodo che è stato definito da altri non necessita di conoscere i dettagli implementativi (body); il programma deve conoscere solo cosa il metodo fa e non come lo fa (esempio: voi utilizzate il metodo SavitchIn.readInt() sapendo che legge da input standard un valore intero, ma non vi siete mai chiesti come lo fa!). Possibilità di modificare l’implementazione (ad esempio per rendere la classe più efficiente) senza che le classi che utilizzano quella modificata se ne accorgano (mi accorgo che l’implementazione del metodo la cui interfaccia è cercacarattere utilizza un algoritmo troppo inefficiente, lo modifico lasciando inalterata l’interfaccia). Stato dell’oggetto sempre consistente: incapsulo gli attributi e permetto la loro modifica attraverso metodi che appartengono all’interfaccia e che effettuano i controlli.
Non è considerata buona norma programmativa dichiarare gli attributi di istanza public: non devono far parte dell’interfaccia della classe. Se si utilizza il modificatore public ogni altra classe può accedere in maniera diretta all’attributo di istanza e quindi modificarlo direttamente. Tali attributi devono essere dichiarati private, devono appartenere all’implementazione (si deve operare anche per loro incapsulamento). Dichiarare un attributo d’istanza private comporta che è accessibile esclusivamente all’interno della classe in cui è definito. Se la norma è dichiarare gli attributi d’istanza private, come faccio al di fuori della classe ad accedere agli attributi stessi? Dichiarare gli attributi d’istanza private obbliga a chi definisca la classe di dotarla di metodi che permettono l’accesso agli attributi stessi, detti metodi di incapsulamento. Un metodo di incapsulamento è semplicemente un metodo che permette ad un’altra classe di leggere o impostare il valore di un attributo di istanza dichiarato private. Tali metodi devono appartenere all’interfaccia pubblica della classe. I metodi di incapsulamento seguono più o meno la seguente convenzione: In modifica: setNomeAttributo; In lettura: setNomeAttributo. Riassumendo Quando si definiscono classi utilizzare sempre le seguenti regole:
Perché l’ereditarietà: Economia: tutti gli attributi, operazioni e associazioni comuni a un certo insieme di classi vengono scritte una sola volta. Consistenza: se un attributo, operazione o associazione della superclasse viene cambiato, la modifica si ripercuote automaticamente su tutte le sottoclassi. Riuso: la superclasse può essere riusata in contesti diversi. Estendibilità: è sempre possibile, anche in un secondo momento, aggiungere nuove sottoclassi senza dover riscrivere la parte comune contenuta nella superclasse. Ogni sottoclasse può ridefinire a piacimento attributi e operazioni (overriding). L’ereditarietà realizza una relazione is_a. Quindi è corretto affermare che un oggetto creato a partire da una sottoclasse sia di tipo della superclasse. Esempio: un oggetto istanziato a partire dalla classe Bicicletta, oltre che essere una Bicicletta, è anche un VeicoloNonMotorizzato e un Veicolo. Una classe può ereditare da più superclassi: ereditarietà multipla. Esempio: lo studente lavoratore eredita dalla classe studente e dalla classe lavoratore. L’ereditarietà multipla è semplice concettualmente, ma porta a una serie di problemi tecnici: cosa succede se una classe eredita due volte dalla stessa superclasse? Cosa succede se due superclassi diverse hanno un attributo con lo stesso nome? Fortunatamente Java non supporta l’ereditarietà multipla. La sintassi utilizzata in Java per esplicitare che una classe deriva da un’altra è la seguente: public class ClassePadre { … } Public class ClasseFiglia extends ClassePadre { … }
In Java qualsiasi classe specializza la classe Object. Fino ad ora, non sapendolo abbiamo scritto classi che sono specializzazione della classe Object, quindi qualsiasi metodo attributo definito nella classe Object è ereditato dalle sottoclassi. Alcuni metodi importanti della classe Object: public boolean equals (object o) public String toString() Quando una classe ne specializza un’altra, eredita tutto a prescindere dalla visibilità. Però se un attributo è definito private nella superclasse non sarà accessibile tramite nome: occorrerà passare attraverso metodi (ovviamente non privati); se un metodo è definito private nella superclasse, non ci sarà alcun modo per permettere alle sottoclassi di invocarlo. Se le sottoclassi hanno la necessita di accedere in modo diretto ad attributi e/o metodi definiti nella superclasse devo utilizzare un altro tipo di visibilità: protected. Esempio: Il modificatore di visibilità si applica ad attributi e metodi con lo stesso significato: Visibilità Chi può accedere Public La classe in cui è definito Tutte le classi (sottoclassi, le classi in associazione, classi nello stesso package, classi in package diversi) Protected La classe in cui è definito Le sottoclassi Le classi appartenenti allo stesso package Private (^) La classe in cui è definito Cosa succede per i costruttori che accettano in ingresso parametri? Regola da rispettare: ogni classe inizializza i propri attributi! Esempio: classe Persona e classe Studente: I costruttori della classe persona inizializzeranno gli attributi nome e cognome I costruttori della classe studente inizializzeranno l’attributo matricola
Effettivamente in Java la si realizza grazie all’utilizzo delle interface. L’interface realizza quasi un’associazione del tipo is_a (al test instance_of 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 }
L’operatore == applicato a due 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: overridare il metodo equals della classe Object Intestazione: public boolean equals(nomeClasse obj). Polimorfismo I tre meccanismi che caratterizzano un linguaggio object-oriented:
Poiché il polimorfismo sia possibile, il compilatore permette di assegnare fra loro reference di tipo diversi (casting = coercizione di tipo: si impone al compilatore di considerare un dato (es un reference) con tipo diverso da quello dichiarato). In Java il casting è ammissibile in due forme: Up-casting : polimorfismo type-safe , ovvero i metodi invocabili per il tipo statico esistono sicuramente per qualsiasi tipo dinamico ammissibile; coercizione di un tipo specializzato verso un tipo più generale (up = più in alto nella gerarchia di eredità). In Java l’upcasting viene garantito implicitamente dal compilatore. NB: dopo un upcasting si possono invocare solo i metodi presenti nel tipo statico (si ricordi però il binding dinamico!). Down-casting : polimorfismo type-unsafe , in un programma che passa la compilazione possono esserci errori nell’uso dei tipo; coercizione di un tipo generale verso un tipo più specializzato (down = più in basso nella gerarchia di ereditarietà). In Java il downcasting è valido ma deve essere dichiarato esplicitamente dal programmatore; il compilatore si fida è responsabilità del programmatore usare il downcasting solo quando ha senso! Se il programmatore effettua un downcasting sbagliato si verificano errori a runtime. In Java, il casting di oggetti non primitivi è “legale” solo per classi legate in una gerarchia di ereditarietà: ovvero solo Upcasting e Downcasting sono “legali”. Un tentativo di effettuare un cast “illegale” viene segnalato come errore a compile-time. Operatore instance_of: permette di testare il tipo dinamico dell’oggetti associato ad un reference. Resituisce vero se il tipo del reference è compatibile con il tipo specificato. In Java tutte le classi sono in relazione di ereditarietà con il tipo Object. Ne consegue che: Upcasting (implicito) di un qualsiasi reference Object è sempre possibile. Dopo un casting a Object è possibile invocare solo i metodi definiti dalla classe Object (per esempio equals o toString), ma ricordate il binding dinamico! Downcasting (esplicito) di un reference di tipo Object a una qualsiasi classe è sempre accettato dal compilatore, anche se ovviamente può produrre errori runtime quando il tipo dinamico dell’oggetto non è compatibile con la classe del casting. Nessun binding dinamico per: Metodi dichiarati static : tali sono associati alle classi (e non agli oggetti) infatti non si invocano attraverso un reference, ma attraverso il nome della classe corrispondente; il nome di una classe rappresenta un unico tipo (non c’è differenza tra tipo statico e dinamico), quindi non è soggetto a polimorfismo. Metodi dichiarati final : tali metodi non possono essere ridefiniti nelle sottoclassi, quindi il binding dinamico non è necessario; un metodo dichiarato final non può essere ridefinito (overridden) in una classe derivata.