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


Appunti Programmazione a Oggetti in Java, Appunti di Elementi di Informatica

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

2019/2020

Caricato il 17/06/2020

francesca-rudello
francesca-rudello 🇮🇹

4.2

(21)

12 documenti

1 / 11

Toggle sidebar

Questa pagina non è visibile nell’anteprima

Non perderti parti importanti!

bg1
Programmazione con Java
Introduzione
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.
Classi e istanze
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.
Astrazioni
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.
Classificazione (“istance_of”):
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.
Generalizzazione(“is_a”):
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:
oI cani sono mammiferi (la classe Cane è una sottoclasse della classe Mammifero
oOgni 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!
oFido è un esemplare di cane (instance_of)
oCane è un mammifero (is_a)
pf3
pf4
pf5
pf8
pf9
pfa

Anteprima parziale del testo

Scarica Appunti Programmazione a Oggetti in Java e più Appunti in PDF di Elementi di Informatica solo su Docsity!

Programmazione con Java

Introduzione

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.

Classi e istanze

 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.

Astrazioni

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.

Classificazione (“ istance_of ”):

 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.

Generalizzazione (“ is_a ”):

 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)

Aggregazione (“ part_of ”):

 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)

Associazione (“ has_a ”):

 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.

Associazione : linea che collega le classi coinvolte.

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.

Realizzare l’incapsulamento:

  1. Distinguere fra interfaccia e implementazione: Gli attributi di istanza sono un dettaglio implementativo, il significato è dato dalle operazioni che manipolano (esempio: il significato di un Semaforo non dipende che l’attributo colore sia di tipo String o int, ma dal funzionamento del metodo cambiaColore). Soluzione: modificatori di visibilità per impedire a chi usa oggetti di accedere direttamente agli attributi in scrittura (esempio: impedire a chi usa oggetti Semaforo di accedere all’attributo colore, mettendo a disposizione solo il metodo cambiaColore).
  2. Permettere solo stati validi per gli oggetti: Lo stato degli oggetti deve contenere sempre valori validi (esempio: blu non è un colore valido per un semaforo). Soluzione: controllare tutte le modifiche dello stato attraverso metodi.

Modificatori public e private

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:

  1. Inserire sempre un commento in testa alla classe che specifica l’astrazione che rappresenta. Usare i commenti per generare la documentazione;
  2. Dichiarare tutti gli attributi di istanza appartenenti all’implementazione (modificatore private).
  3. Fornire gli opportuni metodi di incapsulamento per leggere e modificare gli attributi di istanza (appartenenti quindi all’interfaccia pubblica, modificatore public);
  4. Fornire tutti i metodi dell’interfaccia pubblica (modificatore public);
  5. Documentare tutti i metodi inserendo le precondizioni e le postcondizioni;
  6. Dichiarare i metodi di servizio appartenenti all’implementazione (modificatore private);
  7. Inserire sempre nella definizione di classe il metodo toString(). Overloading Operare overloading di un metodo vuol dire assegnare lo stesso nome a due o più definizioni differenti all’interno della stessa classe. Java determina la definizione corretta sulla base del numero e del tipo di argomenti. Si fa overloading quando diversi metodi semanticamente fanno la stessa cosa ma con parametri in ingresso diversi, non ha senso modificare il nome. Attenzione: non è possibile overloadare un metodo modificando esclusivamente il tipo di ritorno. Costruttori Quando si crea un oggetto è buona norma inizializzare i valori degli attributi. Come detto, gli attributi vengono inizializzati a valori di default ma 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 dei valori iniziali per gli attributi: se devo costruire un’interfaccia grafica, voglio che tutte le operazioni di costruzione vengano fatte nel momento in cui istanzio l’oggetto.

Astrazioni:

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

L’ereditarietà multipla?

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 }

Confrontare lo stato di due oggetti

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:

  1. Incapsulamento (tipo di dato astratto, separazione fra interfaccia e implementazione);
  2. Ereditarietà (gerarchie di tipi e riuso);
  3. Polimorfismo = molteplici forme. Proprietà del codice di comportarsi diversamente in diversi contesti di esecuzione; ovvero lo stesso codice viene utilizzato con istanza di tipi diversi. Tecnicamente, un reference dichiarato di un tipo ( tipo statico ) può dinamicamente (a runtime) riferirsi a oggetti di tipo diverso ( tipo dinamico ). Polimorfismo per inclusione : la corrispondenza fra tipi statici e dinamici è possibile nell’ambito delle gerarchie di tipi definite con il meccanismo di ereditarietà. Ovvero, i possibili tipi dinamici per un reference sono solo i sovra-tipo e sotto-tipi del tipo statico. Semantica is_a: in una gerarchia di tipi, il tipo più generale (sovra-tipo) rappresenta un’astrazione di tutti i tipi che lo specializzano (sotto-tipi). Il polimorfismo si basa sulla semantica is_a delle gerarchie di generalizzazione-specializzazione definite attraverso i meccanismi di ereditarietà di Java. Binding dinamico : anche detto late binding. Il legame fra la definizione e l’invocazione dei metodi non viene stabilito “staticamente” dal compilatore (compile-time), ma bensì “dinamicamente” a runtime.  Il compilatore non ha modo di stabilire quale implementazione del metodo disegna() deve chiamare: quella di Rettangolo o quella di Triangolo, o quella di Poligono?  Il tipo effettivo del parametro è noto solo quando il metodo “disegna” viene invocato, cioè solo a runtime.  Tecnicamente: il compilatore non genera il codice per eseguire il metodo, ma genera il codice che riconosce il tipo effettivo del parametro, cerca l’implementazione giusta di “disegna”, e la esegue (dispatching). In Java il binding è sempre dinamico!! NB: il binding dinamico non è automatico in tutti i linguaggi di programmazione. Ad esempio in C++ viene abilitato inserendo la keyword “virtual” nella signature dei metodi. Nei linguaggi tipizzati il compilatore verifica l’ammissibilità del codice in base ai tipi dichiarati (tipo statico); ad esempio, le invocazioni del metodo ammissibili sono solo quelle ammissibili per il tipo statico del reference.

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.