
































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
Un'introduzione completa alla programmazione orientata agli oggetti in java, coprendo concetti fondamentali come classi, metodi, astrazione e invarianti di rappresentazione. La definizione di classi, la creazione di oggetti, l'incapsulamento, la gestione degli errori tramite eccezioni e l'utilizzo di asserzioni per il debug. Inoltre, vengono approfonditi i concetti di astrazione e implementazione, con particolare attenzione alla funzione di astrazione e all'invariante di rappresentazione. Ricco di esempi pratici e illustrazioni che facilitano la comprensione dei concetti chiave.
Tipologia: Sintesi del corso
1 / 40
Questa pagina non è visibile nell’anteprima
Non perderti parti importanti!

































Programmazione Imperativa: Sequenza di operazioni (istruzioni) in uno specifico ordine Programmazione Procedurale: Istruzioni strutturate in procedure indipendenti, riusabili e parametrizzate
■ I sottoproblemi dovrebbero essere logicamente coesi e svolgibili indipendentemente
■ Combinando le soluzioni dei sottoproblemi si dovrebbe poter risolvere il problema originale
■ I sottoproblemi dovrebbero essere allo stesso livello di dettaglio
Il modo per modellare il processo di Decomposizione è mediante Astrazione ⤵
■ Può essere visto come un mapping molti → uno al fine di definire un meccanismo generale per risolvere una serie di problemi simili
■ Es: In matematica le operazioni sono delle astrazioni che ci permettono di svolgere calcoli con diversi valori degli operandi
Meccanismi di astrazione:
■ Astrazione per parametrizzazione
Trascura i valori effettivi utilizzati nei calcoli e si concentra sul loro tipo , potrò sostituire i parametri con i valori effettivi, mantenendo valido il calcolo
Genero entità che dipendono e lavorano su dei parametri. Ci consente di rappresentare un insieme infinito di calcoli diversi con un singolo testo di programma che è un'astrazione di tutti loro.
👉 è un mezzo importante per raggiungere la generalità nei programmi
■ Astrazione per specifica
Trascura il modo in cui viene risolto, si concentra sulle specifiche del problema. Posso mantenere la validità del programma anche se implementato in maniera diversa
Dichiaro, in maniera esplicita o implicita, il modo in cui queste entità devono essere utilizzate (senza doverne conoscere lʼimplementazione)
Mediante astrazione per parametrizzazione generalizzo i comportamenti del tipo di dato Mediante astrazione per specifica definisco cosa modella il tipo di dato e i suoi comportamenti, senza spiegare come è costruito il tipo di dato o come sono implementati i suoi comportamenti. ■ Astrazione di iterazione: ci consente di i terare sugli elementi di una collezione senza rivelare dettagli su come vengono ottenuti gli articoli Mediante astrazione per parametrizzazione definisco i metodi per iterare sulla sequenza di dati Mediante astrazione per specifica definisco il comportamento dellʼiterazione (in che modo e ordine mi arrivano gli elementi), senza spiegare come questo processo è implementato o come è memorizzata la sequenza di dati. ■ Gerarchia dei tipi di dati: caratterizza la creazione dei nuovi tipi di dato partendo da tipi di dato esistenti ed estendendone le capacità. Mediante astrazione per parametrizzazione posso (ri-)definire i comportamenti del nuovo tipo di dato, possibilmente riutilizzando quelli del dato “Genitoreˮ Mediante astrazione per specifica definisco le differenze e le similitudini nel comportamento, senza spiegare come queste sono implementate
OBJ Orientation
Differenza tra approccio procedurale e orientato a oggetti:
■ Nei linguaggi procedurali la logica del programma è strutturata in:
Dei dati memorizzati nelle variabili Delle azioni sui dati, definite nelle funzioni
■ Nei linguaggi orientati ad oggetti i dati sono strutturati come oggetti aventi sia:
Uno stato , ovvero una serie di variabili ( attributi ) che caratterizzano lʼoggetto Dei comportamenti ( metodi ) che agiscono (ispezionando o modificando) sullo stato dellʼoggetto
Qual è la differenza? (cosa ci permette di fare questa astrazione?
👉 Encapsulation (fondamento della OOP modificare stato dellʼoggetto solo attraverso i suoi metodi!
Per definire questi oggetti in Java scriveremo delle Classi di oggetti, che:
■ Definiscono quali attributi un oggetto di quella classe avrà
■ Definiscono quali metodi potranno essere chiamati su un oggetto di quella classe
■ Le classi si definiscono con ‘public class NomeClasse> {...}ʼ (nome inizia con maiuscola)
Esistono classi che contengono solo metodi statici ( non modificano lo stato di un oggetto ), queste classi, non avendo uno stato, non possono neanche creare oggetti (es main)
Genera eseguibili NON in codice macchina ma bytecode
Per eseguire il bytecode serve una macchina virtuale VM che lo i nterpreta in istruzioni macchina. Il bytecode può girare sulla Java VM di qualsiasi hardware → WORM Write Once Run Many). Le “ librerie ˮ delle classi sono contenute nel percorso visibile dal compilatore e dalla VM
Java è un linguaggio fortemente tipizzato. Ciò significa che gli errori di tipo come l'utilizzo di un puntatore come numero intero vengono rilevati dal compilatore.
Esecuzione: Le classi sono definite in file .java (in genere una per file) Nome file = nome della classe (necessario per classi con visibilità pubblica) Il comando ‘javac
In Java le espressioni sono eseguite da sinistra verso destra e possono essere costituite da:
atomi : variabili, costanti, letterali operatori (+, -, =, ...) invocazioni di metodi
Le espressioni hanno come risultato un tipo e un valore Possibile usarle per costruire altre espressioni (es: come operando, parametro di funzione) A
compile time è possibile calcolare il tipo di una qualunque espressione per induzione Il valore invece potrebbe essere calcolabile solo a run time (dipende da condizioni)
Le variabili in java hanno un nome e un tipo. Questi tipi possono essere:
■ primitivi (int, float, boolean...), generati sulla stack e contengono dei valori
■ riferimenti ad obj (array e oggetti) con riferimento nella stack e contenuto nella heap oggetti si inizializzano con operatore new e chiamata al costruttore
String e array si possono anche creare con un literal (es: ‘s String = "ciao";‘)
A variabile riassegnata o metodo terminato lʼoggetto in heap è cancellabile dal garbage collector
👉 Le variabili riferimento possono puntare allo stesso oggetto
Se creo una variabile ‘a String = "paperino";‘ questa allocherà uno spazio sulla heap. Se assegno una nuova variabile ‘c=a‘ ora queste punteranno allo stesso spazio in heap.
prevedibile. Il concetto che un unico simbolo può avere diversi significati a seconda del contesto dʼutilizzo si chiama Polimorfismo ed è un fondamento della OOP ??
Conversioni Implicite e Overloading
Nel caso di espressioni con tipi diversi in alcuni casi Java può fare casting implicito. Questo è possibile anche nel caso dei parametri dei metodi
Tuttavia, nel caso dellʼoverloading dei metodi ci possono essere molteplici possibilità Ad esempio dati due metodi ‘somma(int a, double b)‘ e ‘somma(double a, double b)‘
■ Si effettua la chiamata più specifica (most specific)
■ Se non sono possibili conversioni o non cʼè una più specifica vi è un errore di compilazione Ad esempio dati due metodi ‘somma(int a, double b)‘ e ‘somma(double a, int b)‘
👉 ⚠ !! Il meccanismo che decide quale metodo invocare si chiama Dispatching !! ⚠
La variante dinamica degli array sono le List (es: ArrayList). List è unʼinterfaccia , per funzionare con i diversi tipi sono collezioni di tipo Object , posso quindi metterci dentro anche oggetti di tipo diverso. Però devo anche fare casting esplicito per usare i metodi del loro tipo giusto
Spesso però non mi serve avere oggetti diversi in una List e fare continuamente casting esplicito rallenta la scrittura del codice ed è prono a errori. È possibile restringere la collezione ad un tipo specifico di dato con il meccanismo di Generics
Gli oggetti primitivi non possono essere usati in molti contesti in Java, es. ArrayList (o altre Collections) lavorano solo su Oggetti
Ci sono degli appositi metodi per passare da tipo primitivo a oggetto e viceversa
👉 Questa meccanica si chiama Wrapping (avvolgimento) e Unwrapping (svolgimento)
Possibile conversione automatica da tipo primitivo a oggetto corrispondente
■ Questa capacità viene chiamata Boxing (inscatolamento) o Autoboxing
■ Lʼoperazione opposta è Unboxing (viene fatto anche comparando con==)
■ MODIFIES (effetti collaterali): specifica gli input modificati dalla procedura
■ EFFECTS (post-condizioni): specifica gli effetti della procedura
■ OVERVIEW (su classe): specifica lʼobiettivo della classe e possibili funzionalità chiave
👉 ATTENZIONE La specifica deve essere il primo passo nella scrittura del codice e serve per progettare più agevolmente la struttura del codice
es linguaggio specifica Liskov
Proprietà delle specifiche: !!!
■ restrittività : dovrebbe escludere le implementazioni non corrette
■ minimalità : dovrebbe porre il meno possibile vincoli sullʼimplementazione. Questo ha come effetto una maggiore generalità delle procedure , inoltre potrebbe permettere la sottodeterminazione , ovvero comportamento non determinato, ma le procedure dovrebbero sempre avere un comportamento deterministico
■ chiarezza : dovrebbe essere chiara semanticamente, > chiarezza tramite la ridondanza - dovrebbe chiarire il significato mediante ulteriori spiegazioni
Proprietà delle procedure:
■ semplicità : deve essere chiaro cosa fa una procedura
■ generalità : determina il contesto di applicabilità di una procedura. Una procedura è più generale se accetta unʼinsieme più ampio di valori di input. Si può rendere più generale mediante parametrizzazione delle variabili o delle assunzioni
Le procedure si distinguono in:
Procedure totali
non richiedono la clausola require, il Compilatore stesso controlla lʼunico vincolo di validità, ovvero il tipo di input Procedure parziali
Richiedono la clausola requires. Bisogna specificare su quali parametri la procedura può funzionare In tutti gli altri casi il comportamento non è definito, Ovvero può succedere qualunque cosa (crash, hang, output di qualche genere). Il comportamento deve essere deterministico Perchè costruire procedure parziali? Anche se le procedure totali sono più sicure, quelle parziali possono essere più performanti e più facili da scrivere
Quando fare una procedura parziale e quando una totale?
■ Totale per ridurre errori e rendere più generale lʼutilizzo
■ Parziale se il contesto dʼ uso è limitato e ben noto , e per grandi benefici di performance
Alto costo di gestione dellʼerrore Comma OK no Esiste in go ma non in Java :(, non si possono restituire più valori in java. Ma in Java è possibile fare un wrapping di più valori di return in un unico tipo di ritorno Problemi: ■ Bisogna ricordarsi di verificare se cʼè stato un errore ■ Altissimo rischio di propagazione ■ Alto rischio di runtime errors
Eccezioni ✅
Meccanismo (parzialmente) separato dal control flow del programma per la gestione degli eventi eccezionali
■ Un evento eccezionale causa unʼinterruzione della procedura ■ È indipendente dal codominio della procedura (dal valore di return) ■ Costringe a gestire lʼiregolarità: Non si propaga perchè è sicuramente individuato
Tipi di eccezioni
Sono oggetti (sottotipo Throwable ). Gerarchia con due gruppi principali: ■ Error: errori fatali nellʼesecuzione del programma ■ Exception : comportamenti inattesi ma gestibili nellʼesecuzione del programma
Altre Exception - avvenimenti prevedibili
Uncheked exceptions: Eventi fuori dal controllo del programmatore Checked exceptions: Tutte le altre, il programmatore le gestisca nel codice, Altrimenti compile error
Come si usano?
Trattamento esplicito in main:
■ Rinchiudere le espressioni che possono causare eccezioni in un blocco ‘ try {...}‘ seguito da un blocco ‘ catch TipoEccezione> e) {...}‘ che uso per gestire lʼeccezione
■ È permesso un blocco finale ‘ finally {...}‘ che viene eseguito sempre, anche se try va bene. Serve per chiudere le risorse aperte
Come si specifica?
Sono necessarie modifiche alla signature e alla specifica dei metodi
■ keyword “ throws ˮ nella signature , seguita dai tipi di eccezione che potrebbero accadere, riportare anche le eccezioni unchecked per rendere chiaro il comportamento
■ Deve esserci la descrizione di ciascuno dei casi in EFFECTS
Se lʼeccezione avviene per alcuni valori del dominio, questi contano come validi.
■ Se vi sono modifiche agli input o effetti collaterali, nella clausola MODIFIES bisogna rendere chiaro se e in quali eccezioni avvengono le modifiche.
Come si lanciano?
Si usa lʼistruzione throw :
■ “throw new Exception("Messaggio ʼerrore");ˮ
Utile lanciare eccezioni di tipo appropriato:
■ Fornire un messaggio dʼerrore utile per gestirlo (su crash o da log)
■ Creare eccezioni nuove più appropriate alla situazione
Come si definiscono nella classe?
Serve estendere una classe base
■ ‘public class NewKindOfException extends Exception ‘ ( checked )
■ Definizione della classe : ‘(public) class NomeClasse {...}ʼ
■ Attributi (fields): ‘TipoDato> nome;ʼ Sono le variabili il cui valore rappresenta lo stato dellʼoggetto Saranno la rappresentazione del dato che andrà a finire in memoria Si impostano quando si crea una nuova istanza della classe (oggetto)
■ Metodi di istanza : servono per accedere agli attributi di un oggetto della classe:
altri metodi : ‘(public) TipoRitorno> nomeMetodo(...) {...}ʼ che agiscono sullo stato
■ metodi di classe (statici) : ‘(public) static TipoRitorno> nomeMetodo() {...}ʼ che non modificano lo stato, non serve riferimento a obj per essere chiamato
In che cosa differiscono i metodi di istanza dai metodi statici?
■ I metodi di istanza agiscono sugli attributi dellʼoggetto
Quindi si possono usare solo su oggetti specifici (istanze)
I metodi di istanza possono essere:
Metodi di costruzione Creators): creano un nuovo oggetto e ne assegnano gli attributi Metodi di mutazione Mutators): cambiano lo stato dellʼoggetto (modificando gli attributi) Metodi di osservazione Observers): restituiscono informazioni sugli attributi dellʼoggetto Metodi di produzione Producers): restituiscono un altro oggetto del loro stesso tipo
Per utilizzare un nuovo oggetto del tipo T devo prima crearlo e poi interagire coi suoi metodi. Si assegna ad una variabile del tipo T un oggetto restituito dal suo costruttore.
•‘Persona giovanni = new Persona("Giovanni", "M", 23, 1.74, "biondo");ʼ
Si possono usare i suoi metodi di osservazione per leggerne gli attributi
Si possono usare i suoi metodi di mutazione per cambiare gli attributi
•‘giovanni.siTinge("rosso");ʼ
👉 Non si accede direttamente agli attributi perché lʼimplementazione può cambiare!
Si può accedere direttamente agli attributi di altri oggetti della stessa classe perché una classe ha sempre accesso completo al suo stesso codice
Perchè facciamo astrazione sui dati?
Altrimenti sarei vincolato su come il dato è implementato.
Se in fase di implementazione stabilisco una determinata implementazione del tipo di dato qualsiasi modifica al tipo di dato richiederebbe di: Modificare come implemento il tipo dato (ovviamente) Modificare tutte le procedure che utilizzano questo tipo di dato Incluso le procedure fatte da altri che utilizzano il mio tipo di dato!
Questo rende difficile il riuso dei tipi di dati e limita la modularità
Ci concentreremo su come specificare lʼastrazione sui dati mediante:
Astrazione per parametrizzazione: metodi e parametri dei metodi Astrazione per specifica: specifiche della classe e dei suoi metodi
Per astrarre un tipo di dati prima decido che cosa voglio modellare e che cosa deve saper fare