Programmazione orientata agli Oggetti Classi Le classi sono

  • Slides: 51
Download presentation
Programmazione orientata agli Oggetti

Programmazione orientata agli Oggetti

Classi Le classi sono uno strumento per costruire strutture dati che contengano non solo

Classi Le classi sono uno strumento per costruire strutture dati che contengano non solo dati ma anche il codice per gestirli. Come tutti i costrutti che permettono di definire le strutture dati, una classe definisce un nuovo tipo di dato. I membri di una classe sono dati (esattamente come i membri di un record), chiamati attributi, e metodi, ovvero procedure, che operano su un oggetto. Dal punto di vista matematico, una classe definisce un insieme in modo intensivo, ovvero definendone le caratteristiche invece che elencandone gli elementi. Se l'accesso agli attributi è ristretto ai soli membri della classe, le caratteristiche dell'insieme possono includere vincoli sui possibili valori che la tupla degli attributi può o non può assumere, e anche sulle possibili transizioni tra questi stati. Una classe può dichiarare riservate una parte delle sue proprietà e/o dei suoi metodi, e riservarne l'uso a sé stesso e/o a particolari tipi di oggetti a lui correlati.

Oggetto Un oggetto è una istanza di una classe. Un oggetto occupa memoria, la

Oggetto Un oggetto è una istanza di una classe. Un oggetto occupa memoria, la sua classe definisce come sono organizzati i dati in questa memoria. Ogni oggetto possiede tutti gli attributi definiti nella classe, ed essi hanno un valore, che può mutare durante l'esecuzione del programma come quello di qualsiasi variabile. Il paradigma OOP suggerisce un principio noto come information hiding che indica che si debba accedere agli attributi dell'istanza solo tramite metodi invocati su quello stesso oggetto. Sintatticamente, i metodi di una classe vengono invocati "su" un particolare oggetto, e ricevono come parametro implicito l'oggetto su cui sono stati invocati. Questo parametro normalmente può essere referenziato esplicitamente; per esempio, a tale scopo in C++, in. Java, e in C# si usa la parola chiave this, mentre in Smalltalk, in Objective-C, Python e in Ruby si usa la parola-chiave self. Gli oggetti effettivamente creati sono membri dell'insieme definito dalla loro classe. Molti linguaggi forniscono un supporto per l'inizializzazione automatica di un oggetto, con uno o più speciali metodi detti costruttori.

L'incapsulamento è la proprietà per cui un oggetto contiene ("incapsula") al suo interno gli

L'incapsulamento è la proprietà per cui un oggetto contiene ("incapsula") al suo interno gli attributi (dati) e i metodi (procedure) che accedono ai dati stessi. Lo scopo principale dell'incapsulamento è appunto dare accesso ai dati incapsulati solo attraverso i metodi definiti, nell'interfaccia, come accessibili dall'esterno. Gestito in maniera intelligente, l'incapsulamento permette di vedere l'oggetto come una black-box, cioè una scatola nera di cui, attraverso l'Interfaccia sappiamo cosa fa e come interagisce con l'esterno ma non come lo fa. I vantaggi principali portati dall'incapsulamento sono: robustezza, indipendenza e l'estrema riusabilità degli oggetti creati. . .

Ereditarietà L'OOP prevede un meccanismo molto importante, l'ereditarietà, che permette di derivare nuove classi

Ereditarietà L'OOP prevede un meccanismo molto importante, l'ereditarietà, che permette di derivare nuove classi a partire da classi già definite. L'ereditarietà permette di aggiungere membri ad una classe, e di modificare il comportamento dei metodi, in modo da adattarli alla nuova struttura della classe. Da una stessa classe è possibile costruire diverse classi derivate. Da una classe derivata è possibile derivarne un'altra con lo stesso meccanismo. Sintatticamente, una classe può essere definita come derivata da un'altra classe esistente. In molti linguaggi la classe derivata, o sottoclasse, eredita tutti i metodi e gli attributi della classe "genitrice", e può aggiungere membri alla classe, sia attributi che metodi, e/o ridefinire il codice di alcuni metodi. L'ereditarietà può essere usata come meccanismo per gestire l'evoluzione ed il riuso del software: il codice disponibile definisce delle classi, se sono necessarie modifiche, vengono definite delle sottoclassi che adattano la classe esistente alle nuove esigenze.

Sottotipazione Se un oggetto di una sottoclasse può essere utilizzato al posto di un'istanza

Sottotipazione Se un oggetto di una sottoclasse può essere utilizzato al posto di un'istanza della superclasse, il tipo della classe derivata è detto sottotipo. Questo richiede che tutti i metodi della superclasse siano presenti nella sottoclasse, e che le signature siano compatibili. Di conseguenza, una sottoclasse che voglia definire un sottotipo può ridefinire i metodi della superclasse, ma non può eliminarli sintatticamente né modificare le loro signature. In numerosi linguaggi, invece, una sottoclasse può decidere di eliminare o cambiare le proprietà di accesso ad un metodo, il che fa sì che l'operazione di subclassing non sia corrispondente a quella di subtyping.

Esempio di Ereditarietà: I mammiferi

Esempio di Ereditarietà: I mammiferi

Classi in java

Classi in java

Classi e Oggetti Java Esclusi i tipi primitivi (boolean, char, byte, short, int, long,

Classi e Oggetti Java Esclusi i tipi primitivi (boolean, char, byte, short, int, long, float, double), in Java esistono solo: • CLASSI – componenti software che possono avere i loro dati e le loro funzioni – vengono usate come “struttura” per costruire oggetti • OGGETTI – entità dinamiche costruite al momento del bisogno secondo la definizione di una classe che ne descrive le proprietà

Nota Gli Oggetti sono elementi distinti di una classe che condividono delle caratteristiche. Ogni

Nota Gli Oggetti sono elementi distinti di una classe che condividono delle caratteristiche. Ogni Metodo è un insieme di istruzioni che una classe o un oggetto rendono disponibili ad altre classi e/o oggetti, affinché possa essere eseguito su richiesta. Non occorre preoccuparsi della distruzione degli oggetti: Java ha un garbage collector.

Definire una classe in java equivale al creare un nuovo tipo di dato (una

Definire una classe in java equivale al creare un nuovo tipo di dato (una nuova classe di oggetti)

Attributi ▪ Gli attributi di una classe definiscono la struttura dello stato degli oggetti

Attributi ▪ Gli attributi di una classe definiscono la struttura dello stato degli oggetti che vi appartengono ▪ Ogni attributo è definito usando la stessa sintassi usata per la definizione delle variabili locali ▪ Convenzioni: scrivere gli attributi in minuscolo (tecnologia e non Tecnologia) ▪ Sintassi: ogni attributo è definito usando la stessa sintassi usata per la definizione delle variabili locali

Attributo

Attributo

Metodi

Metodi

Ricapitoliamo. . Nella classe vengono dichiarati: ▪ i costruttori, che determinano le modalità di

Ricapitoliamo. . Nella classe vengono dichiarati: ▪ i costruttori, che determinano le modalità di creazione degli oggetti; ▪ le variabili o attributi di istanza, che costituiscono lo stato degli oggetti; ▪ i metodi di istanza, che realizzano le funzionalità degli oggetti.

Quando Creiamo un oggetto con il costruttore e l'operatore new "cosa succede ? "

Quando Creiamo un oggetto con il costruttore e l'operatore new "cosa succede ? " La creazione di un oggetto è un processo costituito dalle seguenti fasi: Allocare lo spazio di memoria necessario a contenere l'oggetto Inizializzare le variabili di istanza dell'oggetto Per contenere un oggetto la quantità di memoria necessaria è determinata soltanto dalla dichiarazione della classe (precisamente, dal numero e dal tipo delle variabili di istanza) Lo spazio necessario viene allocato in una zona di memoria chiamato heap.

Operatore New L’operatore new è alla base del linguaggio Java. Mediante esso viene creata

Operatore New L’operatore new è alla base del linguaggio Java. Mediante esso viene creata una nuova istanza di un oggetto appartenente ad una determinata classe e, conseguentemente, viene allocata automaticamente la memoria necessaria nell’heap per conservare tale istanza. L’operatore new, per eseguire la creazione di un oggetto, invoca il costruttore della classe che si desidera istanziare.

Cosa abbiamo fatto fino ad ora

Cosa abbiamo fatto fino ad ora

public class Contatore { private int valore; public String tecnologia; public void incrementa (){

public class Contatore { private int valore; public String tecnologia; public void incrementa (){ valore++; } public void reset() { valore=0; } public int get. Valore() { return valore=0; } public Contatore() { valore=0; } } Esempio di classe

Visibilità di metodi e attributi

Visibilità di metodi e attributi

Modificatori di Accesso. I modificatori sono parole riservate che forniscono al compilatore informazioni sulla

Modificatori di Accesso. I modificatori sono parole riservate che forniscono al compilatore informazioni sulla natura del codice, dei dati e delle Classi contenuti nei file sorgenti. Fra tutti i modificatori alcuni possono essere raggruppati in una singola categoria che ne specifica il comportamento, essi sono i modificatori di accesso: ▪ private ▪ protected ▪ public In assenza di questi tre, un elemento di programma viene considerato package-local o friend (si dice che assume la visibilità di default).

Private private è il modificatore di accesso più restrittivo. I metodi e gli attributi

Private private è il modificatore di accesso più restrittivo. I metodi e gli attributi dichiarati private, non sono visibili né usabili all'esterno della classe che li contiene. Due oggetti della stessa classe però, possono vedere i rispettivi attributi e metodi privati. Tuttavia è possibile accedervi dall'esterno mediante metodi di lettura e modifica dei valori: metodi "get" e "set". La possibilità di usare valori con il modificatore private è un punto importante della programmazione a oggetti. In questo modo i valori private vengono incapsulati, rendendo impossibile il loro richiamo o modifica da parte di altre porzioni di codice, a causa di disattenzione o casualità, o perfino di codice malevolo. Ovvero per leggerli o modificarli occorre implementare e chiamare appositamente i metodi get e set. Dunque ove possibile bisogna sempre editare come private gli elementi del programma, è una indicazione forte ma non un obbligo.

Protected Il modificatore protected può essere attribuito solo ai metodi e alle variabili interne

Protected Il modificatore protected può essere attribuito solo ai metodi e alle variabili interne alla classe e non può essere applicato alla classe stessa. I metodi e le variabili dichiarate come protected sono visibili da classi dichiarate nello stesso package e da sottoclassi e classi derivate dichiarate ovunque. Si può dire che è leggermente più restrittivo di public.

Default Si applica a tutti gli elementi della classe e ad essa stessa. Il

Default Si applica a tutti gli elementi della classe e ad essa stessa. Il modificatore di default viene assegnato automaticamente dal compilatore solo quando nella scrittura del sorgente si omettono gli altri modificatori. Di default indica che l'elemento è di accesso pubblico ma solo ed esclusivamente al package della classe dove è inserito, diversamente da public e protected dove è visibile anche in package esterni. Si può dire che è leggermente più restrittivo di protected.

Public Il modificatore public può essere attribuito a tutti gli elementi di una classe.

Public Il modificatore public può essere attribuito a tutti gli elementi di una classe. public definisce l'elemento del programma come "pubblico" e quindi questo è visibile e modificabile dall'esterno della classe, da qualsiasi package.

Ereditarietà

Ereditarietà

Principio che consente a una classe di ereditare le variabili e i metodi di

Principio che consente a una classe di ereditare le variabili e i metodi di un’altra classe Consente di raffinare le caratteristiche di classi di oggetti a partire da classi generali riutilizzo del codice

Superclasse Sottoclasse

Superclasse Sottoclasse

Esempio

Esempio

A cosa serve l'ereditarietà Permette di riutilizzare il codice delle classi già definite ▪

A cosa serve l'ereditarietà Permette di riutilizzare il codice delle classi già definite ▪ Posso definire un nuovo Pesce senza preoccuparmi di dover ridefinire come respira ▪ Le nuove classi aggiungono funzionalità ma sono COMPATIBILI con le vecchie ▪ Posso definire un nuovo tipo di Uccello e utilizzarlo come se fosse un Uccello o un Animale e basta ▪ Posso non dover/voler conoscere il tipo specifico! principio di sostituibilità

Sintassi

Sintassi

Costruttori e operatore super La classe derivata eredita tutte le variabili e i metodi

Costruttori e operatore super La classe derivata eredita tutte le variabili e i metodi della classe base La classe derivata NON eredita il costruttore Se la classe base ha un costruttore senza parametri la classe derivata richiama quello Se la classe base NON ha un costruttore senza parametri la classe derivata deve esplicitamente richiamarlo: super

Classe astratta Una classe astratta non può in alcun modo essere istanziata, quindi può

Classe astratta Una classe astratta non può in alcun modo essere istanziata, quindi può essere utilizzata esclusivamente come classe base. Quando estendiamo (o deriviamo) da una classe astratta, la classe derivata deve fornire una implementazione per tutti i metodi astratti dichiarati nella classe genitrice; se così non dovessere, anche la sotto-classe deve essere dichiarata come abstract.

Abstract Una classe astratta (abstract) è una classe di cui non possono essere istanziati

Abstract Una classe astratta (abstract) è una classe di cui non possono essere istanziati oggetti Possono essere istanziati oggetti solo delle sue sottoclassi Permette di definire un comportamento generale

public abstract class Abstract. Process { public void process() { init(); base. Action(); step.

public abstract class Abstract. Process { public void process() { init(); base. Action(); step. After(); } public void init() { // metodo dichiarato direttamente // all'interno della classe astratta } public abstract void base. Action(); // metodo astratto la cui implementazione // è demandata alla sottoclasse public void step. After() { // metodo dichiarato direttamente // all'interno della classe astratta } } class My. Process extends Abstract. Process { @Override public void base. Action() { // implementazione del metodo base. Action() } } Notiamo che il metodo base. Action() nella classe Abstract. Process è astratto, quindi la sottoclasse che la estende deve effettuarne l’Overidde e darne l’implementazione (altrimenti anch’essa dovrebbe essere dichiarata abstract). A questo punto la chiamata al metodo process(): My. Process my. Process = new My. Process(); // Abstract. Process my. Process = new My. Process(); // equivalente alla precedente my. Process. process();

Interfaccia Molto spesso le interfacce vengono confuse con le classi astratte dato che dal

Interfaccia Molto spesso le interfacce vengono confuse con le classi astratte dato che dal punto di vista logico sono molto simili, ma le interfacce possono essere intese come un'evoluzione delle classi astratte e permettono, di fatto, di simulare l'ereditarietà multipla. Il vantaggio principale di una interfaccia, essendo comunque una classe anche se con proprietà particolari, è quello che oltre ad essere estesa può essere implementata. La differenza tra estendere e implementare è molto grande in quanto una classe può essere eriditata solo ed esclusivamente una volta, mentre una classe può implementare infinite interfacce permettendo così l'ereditarietà multipla.

Vediamo adesso, dal punto di vista del codice, come si dichiara un'interfaccia e come

Vediamo adesso, dal punto di vista del codice, come si dichiara un'interfaccia e come la si implementa. La dichiarazione avviene usando la parola chiave interface in questo modo: public interface Prova { //contenuto } L'implementazione dell'interfaccia è invece possibile utilizzando la parola chiave implements: public class Implementazione implemets Prova { //contenuto }

 Interfacce Classi astratte Istanziabile no no Fields solo static final sì Costruttore no

Interfacce Classi astratte Istanziabile no no Fields solo static final sì Costruttore no sì Metodi statici Java 8+ sì Dichiarazione metodi (virtual) no sì Implementazione metodi sì java 8+ (con il qualificatore default)

Quando utilizzare l’interfaccia e quando la classe astratta? In base a quanto detto, cosa

Quando utilizzare l’interfaccia e quando la classe astratta? In base a quanto detto, cosa è meglio utilizzare? Ed in quali circostanze? ▪ Si usa una classe astratta per condividere codice fra più classi, se più classi hanno in comune metodi e campi o se si vogliono dichiarare metodi comuni che non siano necessariamente campi static e final. ▪ Si decide di utilizzare una interfaccia se ci si trova nella situazione in cui alcune classi (assolutamente non legate fra di loro) si trovano a condividere i metodi di una interfaccia, se si vuole specificare il comportamento di un certo tipo di dato (ma non implementarne il comportamento) o se si vuole avere la possibilità di sfruttare la “multiple inheritance”.

Polimorfismo Java

Polimorfismo Java

Cosa è esattamente il polimorfismo? Il polimorfismo ci permette di fare riferimento ad un’entità

Cosa è esattamente il polimorfismo? Il polimorfismo ci permette di fare riferimento ad un’entità utilizzandone un’altra; il concetto in sè non è semplice da comprendere ma cerchiamo di analizzarlo più nel dettaglio, distinguendo due tipi di polimorfismo: ▪ Polimorfismo per metodi ▪ Polimorfismo per dati

Overriding (polimorfismo dei metodi)

Overriding (polimorfismo dei metodi)

Final

Final

Casting

Casting

Un po' di esercizi

Un po' di esercizi

Ripassiamo OVERRIDING : si scrive in una sottoclasse un metodo della superclasse con la

Ripassiamo OVERRIDING : si scrive in una sottoclasse un metodo della superclasse con la stessa segnatura OVERLOADING: è possibile definire metodi con lo stesso nome ma con segnature differenti Cos’è la SEGNATURA? È la «firma» del metodo costituita dal nome del metodo, dal numero dei suoi parametri e dal loro tipo Lo stesso nome di un metodo può essere usato per operazioni diverse, con definizioni diverse che richiedono un diverso tipo o numero di parametri.

 Definire una classe Dipendente che ha i seguenti campi: Nome Cognome ore. Lavorative.

Definire una classe Dipendente che ha i seguenti campi: Nome Cognome ore. Lavorative. Mensili retribuzione. Oraria Scrivere un metodo che calcola lo stipendio del dipendente nel seguente modo: Stipendio=ore. Lavorative. Mensili * retribuzione. Oraria Si definisca anche un particolare tipo di dipendente responsabile di progetto, al cui stipendio del dipendente (vedi sopra) viene aggiunto un bonus: Stipendio=ore. Lavorative. Mensili * retribuzione. Oraria + bonus Scrivere un main() che calcoli lo stipendio per una persona Dipendente e per una persona Dipendente. Responsabile.

class Animale{ private String nome; public Animale(String s){ nome = s; } public String

class Animale{ private String nome; public Animale(String s){ nome = s; } public String come. Ti. Chiami() { return nome; } public void parla(){} public void incontra(Animale a){ System. out. println(nome + “: <Ciao, ” + a. nome + “>”); parla(); } }

Si costruiscano due classi Topo e Gatto che estendono la classe Animale. In particolare

Si costruiscano due classi Topo e Gatto che estendono la classe Animale. In particolare le due classi devono ridefinire il metodo parla(): Il topo deve ridefinire il metodo in modo che esso stampi sullo schermo «Squit» Il gatto deve ridefinire il metodo in modo che esso stampi sullo schermo «Miao» Si aggiunga ora nella classe Topo una nuova versione del metodo incontra() che prende come argomento un oggetto di tipo Gatto e stampa: System. out. println(nome + “: <Aiutoooooo!!!>”); Analogamente per il Gatto si aggiunga una nuova versione del metodo incontra() che prende come argomento un oggetto di tipo Topo e stampa: System. out. println(nome + “: <Ti prendo!!!>”); Scrivere un main() per verificarne il funzionamento