Programmazione a Oggetti e JAVA Prof. B.Buttarazzi A.A. 2012/2013
Sommario Variabili statiche Ereditarietà multipla Le interfacce Binding Esercizi
Variabili statiche Se una variabile di una classe ha il modificatore static, non esisterà una copia per ogni oggetto creato, ma sarà unica per tutte le istanze della classe. public class MyClass { public static int i = 10; public int j = 10; public void incrementa(){ i++; j++; public class TestMyClass { public static void main(string[] args) { MyClass a = new MyClass(); MyClass b = new MyClass(); a.incrementa(); // a.i == 11 e a.j == 11 b.incrementa(); // b.i == 12 e b.j == 11 // a.i e b.i sono la stessa variabile: MyClass.i
Variabili statiche Se un metodo ha il modificatore static può essere utilizzato senza creare un istanza della classe Un metodo statico non può accedere a variabili non statiche. public class MyClass { public static int i = 10; public int j = 10; public static void incrementa(){ i++; j++; // non consentito public class TestMyClass { public static void main(string[] args) { MyClass.incrementa();
Quando si usano metodi e attributi statici Un metodo che non dipende dallo stato dell oggetto (non accede agli attributi non statici dell oggetto) è un buon candidato a diventare static. In genere nelle librerie di funzioni i metodi sono tutti statici(es: i metodi della classe Math). Gli attributi il cui valore deve essere condiviso da tutti gli oggetti di una classe devono essere dichiarati static. In genere le costanti (final) vengono dichiarate static
Esercizio ContoCorrente Creare una classe ContoCorrente che permetta di gestire un conto corrente bancario caratterizzato dai seguenti attributi: nome dell intestatario (String); cognome dell intestatario (String); numero di conto corrente (long); saldo residuo (double). Il costruttore deve avere come parametri il nome, il cognome ed il saldo iniziale. Il numero di conto corrente deve essere calcolato automaticamente in maniera incrementale (si può realizzare usando una variabile statica). Realizzare i metodi: deposita, che permette di depositare denaro sul conto corrente; preleva, che permette di prelevare denaro dal conto corrente; print, che stampa a video le informazioni sul conto corrente (numero, intestatario, saldo residuo)
Esercizio ContoCorrente Per testare il corretto funzionamento della classe ContoCorrente, realizzare la classe TestConto ed il relativo metodo main che esegue le seguenti operazioni: Crea due conti correnti intestati a due persone diverse. Effettua un operazione di bonifico dal primo al secondo conto (utilizzano i metodi preleva e deposita). Stampa a video le informazioni dei conti prima e dopo il bonifico.
public class ContoCorrente { /* Dati personali intestatario */ private String nome; private String cognome; /* Numero di conto corrente */ private long numeroconto; /* Saldo attuale */ private double saldoattuale; /* Contatore interno per l'assegnazione del * numero di c/c per ogni nuovo conto */ private static long prossimonumeroconto = 1; /* Costruttore */ public ContoCorrente(String n, String c, double saldoiniz) { nome = n; cognome = c; numeroconto = prossimonumeroconto; saldoattuale = saldoiniz; /* Incremento il contatore statico: la prossima * invocazione del costruttore assegnerà * il valore successivo */ prossimonumeroconto++; Soluzione
public class ContoCorrente { public void deposita(double ammontare) { saldoattuale += ammontare; public void preleva(double ammontare) { saldoattuale -= ammontare; /* Stampa a video */ public void print() { System.out.println("Il signor " + cognome + " " + nome + " è titolare del c/c n. " + numeroconto + " con saldo pari a " + saldoattuale); Soluzione
public class TestConto { public static void main(string[] args) { ContoCorrente cc1; cc1 = new ContoCorrente("Mario", "Rossi", 5000); ContoCorrente cc2; cc2 = new ContoCorrente("Fabio", "Pagani", 15000); cc1.print(); cc2.print(); cc1.preleva(500); cc2.deposita(500); cc1.print(); cc2.print(); Soluzione
Incapsulamento ed Ereditarietà L incapsulamento rappresenta una tecnica per rendere robusto il programma mentre l ereditarietà è considerata un valido strumento di sviluppo e semplificazione;
Incapsulamento L incapsulamento è uno dei principio fondamentali della programmazione a d oggetti. Il contenuto informativo di una classe deve rimanere nascosto all utente, in modo tale che i metodi siano l unica via per interagire con gli oggetti corrispondenti. L incapsulamento ha due grandi vantaggi: permette al programmatore di disciplinare l accesso agli attributi di una classe, in modo da evitare che ne venga fatto un uso sbagliato; permette all utente (della classe) di concentrarsi esclusivamente sull interfaccia di programmazione, tralasciando ogni aspetto legato all implementazione.
Vantaggi dell ereditarietà L ereditarietà facilita il riutilizzo di software estendendone o ridefinendone caratteristiche e comportamenti; è possibile adattare una classe preesistente alle nuove esigenze. Specificare le differenze da una classe simile piuttosto che ridefinire completamente la classe facilita enormemente lo sviluppo di nuovi progetti eliminando ridondanza di codice. L ereditarietà non è un meccanismo di inclusione del codice di una classe base in una derivata. Non c è copia di codice, ogni modifica della struttura di una classe base si ripercuote automaticamente nelle sue classi derivate
Ereditarietà: estensione Una classe derivata può differenziarsi dalla classe base in quanto aggiunge nuove caratteristiche senza alterare il comportamento delle funzionalità offerte dalla classe base, Si definisce ereditarietà per estensione la situazione in cui nella classe derivata vengono aggiunti nuovi attributi e/o nuovi metodi
Ereditarietà: ridefinizione Una classe derivata potrebbe però fornire le stesse caratteristiche della classe base e differenziarsi invece solo per il comportamento. In questo caso i metodi della classe derivata avranno la stessa firma (nome e lista di tipi dei parametri) ma differente corpo. Si definisce ereditarietà per ridefinizione (overriding) la situazione in cui uno o più metodi della classe base siano ridefiniti nella classe derivata
Estensione e ridefinizione È possibile incontrare classi derivate in cui è applicato sia l overloading (overloading) di metodi che la ridefinizione (overriding) di metodi ovviamente su metodi diversi L esempio relativo alle classi Persona e Studente è un esempio di ereditarietà per estensione e ridefinizione
Un esempio di overriding
Overriding e overloading Attenzione a non confondere il sovraccarico dei metodi (overloading) situazione in cui oltre al corpo del metodo è differente anche la sua firma con la ridefinizione (overriding) situazione in cui la firma del metodo è identica ma è differente il corpo
Binding Il meccanismo che determina quale metodo deve essere invocato in base alla classe di appartenenza dell'oggetto si chiama binding ( bind significa legare ). Si dice che si ha Binding statico ( early binding ) quando il metodo da invocare viene determinato in fase di compilazione. Si dice che si ha Binding dinamico ( late binding ) quando il metodo viene determinato durante l'esecuzione (a run time).
Binding Overloading Stesso nome per due metodi distinti Static binding (method resolution) Overriding Permette la ridefinizione di metodi nella sottoclasse Dynamic binding
Binding Il meccanismo di overriding è concettualmente molto diverso da quello di overloading, e non deve essere confuso con esso. L'overloading consente di definire in una stessa classe più metodi aventi lo stesso nome, ma che differiscano nella firma, cioè nella sequenza dei tipi dei parametri formali. Per l overloading è il compilatore che determina quale dei metodi verrà invocato, in base al numero e al tipo dei parametri attuali. Per l'overriding, invece, che consente di ridefinire un metodo in una sottoclasse: il metodo originale e quello che lo ridefinisce hanno necessariamente la stessa firma, e solo a tempo di esecuzione si determinerà quale dei due deve essere eseguito.
Gerarchia di classi L ereditarietà può estendersi a più livelli generando quindi una gerarchia di classi. Una classe derivata può, a sua volta, essere base di nuove sottoclassi. Sportivo è sottoclasse di Persona ed è superclasse di Nuotatore, Motociclista e Calciatore. Nella parte alta della gerarchia troviamo le classi generiche, scendendo aumenta il livello di specializzazione.
Un esempio class Persona { class Sportivo extends Persona { class Nuotatore extends Sportivo { class Motociclista extends Sportivo { class Calciatore extends Sportivo {
Gerarchia di classi: Esercizio Codificare in Java la gerarchia di classi riportata in figura.
Esempio class Computer { class PC extends Computer { class Server extends Computer { class Notebook extends PC { class Desktop extends PC {
Classi astratte Man mano che si sale nella gerarchia dell ereditarietà, le classi diventano sempre più generiche e spesso più astratte. Ad un certo punto la classe superiore diventa a tal punto generica che la si può pensare come una base (classe astratta) per le altre classi piuttosto che come una classe concretamente utilizzabile.
Classi e metodi abstract Classi astratte Sono usate come superclassi astratte per sottoclassi concrete e per dichiarare riferimenti a variabili. Diverse gerarchie di ereditarietà hanno superclassi astratte che occupano i livelli più alti. Va usata la parola chiave abstract Per dichiarare la classe abstract Usata anche per dichiarare i metodi abstract (solo definiti non implementati) Tipicamente le classi abstract contengono uno o più metodi abstract Tutte le sottoclassi concrete DEVONO fare l override dei metodi astratti
Esempio abstract class Figura { protected double base; protected double altezza; Figura(double b,double h) { base=b; altezza=h; Figura protected double base protected double altezza + Figura(double b, double h) + setbase(double ba) abstract public void setbase(double ba) { base=ba; public double getbase() { return base; + double getbase() + setaltezza(double al) + double getaltezza() + double area (double a, double h) abstract
Esempio public void setaltezza(double al) { altezza=al; public double getaltezza() { return altezza; Figura protected double base protected double altezza + Figura(double b, double h) + setbase(double ba) + double getbase() + setaltezza(double al) + double getaltezza() abstract abstract double area(double a,double h); + double area (double a, double h) abstract
Esempio class Rettangolo extends Figura { Rettangolo(double b,double h) { super(b,h); Figura protected double base protected double altezza + Figura(double b, double h) + setbase(double ba) + double getbase() abstract public double area(double b,double h) { double ar=base*altezza; return ar; Rettangolo + setaltezza(double al) + double getaltezza() + double area (double a, double h) abstract Triangolo + Rettangolo(double b, double h) + double area (double a, double h) + Triangolodouble b, double h) + double area (double a, double h)
Esempio class Triangolo extends Figura { Triangolo(double b,double h) { super(b,h); Figura protected double base protected double altezza + Figura(double b, double h) + setbase(double ba) + double getbase() abstract + setaltezza(double al) public double area(double b,double h) { + double getaltezza() + double area (double a, double h) double ar=(base*altezza)/2; abstract return ar; Rettangolo Triangolo + Rettangolo(double b, double h) + double area (double a, double h) + Triangolodouble b, double h) + double area (double a, double h)
Esempio Metodi e classi astratte sono in corsivo class UsaFigura { public static void main(string[] args) { double arearet,areatri; Figura protected double base protected double altezza + Figura(double b, double h) abstract Metodi e classi astratte sono in corsivo Rettangolo objr=new Rettangolo(6.6,3.0); + setbase(double ba) arearet=objr.area(6.6,3.0); + double getbase() System.out.println("l'area del rettangolo:" +arearet); + setaltezza(double al) + double getaltezza() triangolo objt=new triangolo(10.2,7.5); areatri=objt.area(10.2,7.5); + double area (double a, double h) abstract System.out.println("l'area del triangolo:" +areatri); Rettangolo Triangolo + Rettangolo(double b, double h) + Triangolodouble b, double h) + double area (double a, double h) + double area (double a, double h)
class UsaTriangolo { public static void main(string[] args) {int ar,al,bas; Triangolo objs=new Triangolo(3,6); ar=objs.area(); System.out.println("valore dell'area:" +ar); Esempio Figura protected double base abstract objs.setaltezza(10); protected double altezza objs.setbase(20); ar=objs.area(); System.out.println("la nuova area:" +ar); + Figura(double b, double h) + setbase(double ba) + double getbase() al=objs.getaltezza(); bas=objs.getbase(); System.out.println("la nuova altezza:" +al); System.out.println("la nuova base:" +bas); + setaltezza(double al) + double getaltezza() + double area (double a, double h) abstract Rettangolo Triangolo + Rettangolo(double b, double h) + double area (double a, double h) + Triangolodouble b, double h) + double area (double a, double h)
Ereditarietà singola e multipla In genere nella progettazione OO sono possibili due tipi di ereditarietà: ereditarietà singola ereditarietà multipla
Ereditarietà multipla L ereditarietà multipla si ha quando una sottoclasse deriva da più superclassi
Ereditarietà singola e multipla In genere nella progettazione OO sono possibili due tipi di ereditarietà: ereditarietà singola ereditarietà multipla L ereditarietà singola impone ad una sottoclasse di derivare da una sola superclasse. L esempio presentato precedentemente è un caso di ereditarietà singola: ogni sottoclasse ha una sola classe base, mentre è possibile da una superclasse avere più classi derivate. Vari linguaggi ad oggetti pongono il vincolo dell ereditarietà singola per problemi di chiarezza e semplicità d implementazione, Java è uno di questi. Quindi non è possibile una definizione di classe del tipo: class A extends B,C
Java e l ereditarietà multipla Java non prevede l ereditarietà multipla fra classi L ereditarietà multipla si ha quando una sottoclasse deriva da più superclassi La classe Quadrato ha due superclassi: PoligonoRegolare e Parallelogramma Si può ovviare a questa limitazione con l introduzione delle Interfacce
Esempio di interfaccia interface Figure { /* gli oggetti delle classi che realizzano questa interfaccia sono caratterizzati da RadiceFigure un tipo un area protected double dim1 */ protected double dim2 String tipo(); double area(); + RadiceFigurae(double b, double h) + String tipo() + double area () Figure class RadiceFigure { protected double dim1; protected double dim2; RadiceFigure(double a, double b) { dim1 = a; dim2 = b; Rettangolo + Rettangolo(double b, double h) + String tipo + double area (double a, double h)
Esempio di uso di interfaccia class Rectangle extends RadiceFigure implements Figure{ Rectangle(double a, double b) { super(a, b); protected double dim1 RadiceFigure Figure // definisce area() di Figure public double area() { return dim1 * dim2; protected double dim2 + RadiceFigurae(double b, double h) + String tipo() + double area () // definisce tipo() di Figure public String tipo() { return "Rectangle "; Rettangolo + Rettangolo(double b, double h) + String tipo + double area (double a, double h)
Esempio di uso di più interfacce class Rectangle extends RadiceFigure implements A,B,C{ Rectangle(double a, double b) { super(a, b); // definisce area() di Figure public double area() { return dim1 * dim2; // definisce tipo() di Figure public String tipo() { return "Rectangle ";