Esercitazioni Ingegneria del So2ware 2 - Programmazione Java Ereditarietà, Visibilità, Interfacce Giordano Tamburrelli tamburrelli@elet.polimi.it hfp://giordano.webfacional.com 1
Java: Ereditarietà Richiamo di teoria: Una classe A dichiarata sofoclasse di un'altra classe B: eredita (ha implicitamente) tufe le variabili di istanza/afribui e tur i metodi di B; può avere variabili o metodi aggiunivi; può ridefinire i metodi ereditai da B afraverso l'overriding, in modo tale che essi eseguano la stessa operazione concefuale in un modo specializzato. In Java una classe A si dichiara sofoclasse di B scrivendo: class A extends B 2
Java: Ereditarietà Esercizio: Modellizzare in Java le MountainBike. Le MountainBike sono delle normali biciclefe tufavia nel loro stato interno hanno un valore intero che indica il livello dell ammorizzatore. Soluzione 1: Quella che non vorreste mai implementare davani al professore. Soluzione 2: Quella correfa. 3
Java: Ereditarietà Esercizio: Modellizzare in Java le MountainBike. public class MountainBike { int cadence=0; int speed=0; int gear=1; int damperlv=1; void changedamperlevel(int newvalue){ damperlv=newvalue; Ho copiato e incollato il 90% del codice della biciclefa. E se la definizione di biciclefa dovesse cambiare? Dovrei cambiare MountainBike void changecadence(int newvalue) { void changegear(int newvalue) { void speedup(int increment) { void applybrakes(int decrement) { void printstates() { 4
Java: Ereditarietà Esercizio: Modellizzare in Java le MountainBike. public class MountainBike extends Bicycle{ int damperlv = 1; void changedamperlevel(int newvalue){ damperlv=newvalue; La keywork extends definisce che la MountainBike è una biciclefa. Ha pertanto gli stessi campi interni e gli stessi metodi. In aggiunta a una normale biciclefa ha un livello di ammorizzazione e un metodo che mi permefe di cambiare tale livello. 5
Java: Ereditarietà Esercizio: Cosa stampa? class MountainBikeDemo { public static void main(string[] args) { Bicycle bike1 = new Bicycle(); Bicycle bike2 = new MountainBike(); /* * Testiamo */ bike1.changecadence(50); bike1.speedup(10); bike1.changegear(2); bike1.printstates(); bike2.changecadence(40); bike2.speedup(10); bike2.changegear(3); bike2.changedamperlevel(2); bike2.printstates(); Errore: L oggefo bike2 è stato dichiarato come Bicycle. Pertanto non possiede il metodo changedamperlevel. A run2me gli viene assegnata una MountainBike. Soluzione 1: DowncasIng ((MountainBike) bike2).changedamperlevel(); Soluzione 2: Dichiarare come MountainBike se sappiamo che la useremo cosi. 6
Java: Ereditarietà Esercizio: Cosa stampa? class MountainBikeDemo { public static void main(string[] args) { Bicycle bike1 = new Bicycle(); MountainBike bike2 = new Bicycle(); /* * Testiamo */ bike1.changecadence(50); bike1.speedup(10); bike1.changegear(2); bike1.printstates(); bike2.changecadence(40); bike2.speedup(10); bike2.changegear(3); bike2.changedamperlevel(2); bike2.printstates(); Errore: L oggefo bike2 è dichiarato come MountainBike. Pertanto deve essere garanito che offrirà tur i servizi di una MountainBike. Se gli assegnate una istanza di biciclefa semplice non potrà offrire il servizio di changedamperlevel(); Soluzione: MountainBike bike2=new MountainBike(); 7
Java: Ereditarietà Esercizio: Cosa stampa? class MountainBikeDemo { public static void main(string[] args) { Stampa: Bicycle bike1 = new Bicycle(); MountainBike bike2 = new MountainBike(); /* * Testiamo */ bike1.changecadence(50); bike1.speedup(10); bike1.changegear(2); bike1.printstates(); bike2.changecadence(40); bike2.speedup(10); bike2.changegear(3); bike2.changedamperlevel(2); bike2.printstates(); La stampa della MountainBike è idenica alla stampa della Bicycle. CorreFo, in quanto non abbiamo toccato il metodo printstates(). Ci interesserebbe però che quando richiamiamo printstates() su una MountainBike ci venisse stampata anche l informazione sul livello dell ammorizzatore. 8
Java: Overriding Richiamo di teoria: L overriding permefe a una determinata sofoclasse di ridefinire una determinata implementazione di un metodo che è già presente in una delle sue superclassi. L implementazione nella sofoclasse overrides (sosituisce) l implementazione della superclasse. Questo significa che quando andremo a invocare un metodo su un determinato oggefo, il metodo che verrà effervamente eseguito dipende dall oggefo sul quale lo siamo invocando. In Java la sofoclasse deve definire un metodo con lo stesso nome, gli stessi parametri (nome e Ipo), e lo stesso Ipo di ritorno (o sofoipo). 9
Java: Overriding Esercizio: Ridefinire il metodo printstates di MountainBike. public class MountainBike extends Bicycle{ int damperlv = 1; void changedamperlevel(int newvalue){ damperlv=newvalue; Poiché printstates() ha la stessa testata di quella che troviamo nella classe padre questa definizione sosituisce quella di Bicycle. void printstates() { System.out.println("cadence:"+cadence + speed:"+speed +" gear:"+gear +"damperlevel: +damperlv); 10
Java: Overriding Esercizio: Ridefinire il metodo printstates di MountainBike (migliorato) public class MountainBike extends Bicycle{ int damperlv = 1; void changedamperlevel(int newvalue){ damperlv=newvalue; Se un determinato metodo fa overriding allora è possibile richiamare il metodo che state ridefinendo mediante la keyword super. void printstates(){ super.printstates(); System.out.println("and damperlevel: +damperlv); Il vantaggio è di poter quindi estendere il metodo che state ridefinendo. 11
Java: super vs this Esercizio: Date le segueni definizioni di classe class Persona{ String nome; String cognome; Persona(){ nome="sconosciuto"; cognome="sconosciuto"; Persona(String nome, String cognome){ this.nome=nome; this.cognome=cognome; class Studente extends Persona{ int matricola; Studente(int matricola){ //super() this.matricola=matricola; Studente(String nome, String cognome, int matricola){ super(nome, cognome); this(matricola); void mipresento(){ System.out.println( "Mi chiamo " + nome + " " + cognome); void mipresentostudente(){ super.mipresento(); this.mipresento(); void mipresento(){ System.out.println( "Sono uno studente con matricola " + matricola + ". "); 12
Java: super vs this Esercizio: cosa stampa? public class SuperAndThis { public static void main(string[] args){ Persona p=new Persona("Alfredo", "Motta"); p.mipresento(); Studente s=new Studente("Alfredo", "Motta", 123); s.mipresento(); Studente t=new Studente(123); t.mipresento(); Stampa: Mi chiamo Alfredo MoFa Mi chiamo Alfredo MoFa Sono uno studente con matricola 123. Mi chiamo Sconosciuto Sconosciuto Sono uno studente con matricola 123. 13
Java: Overriding vs Overloading class Super{... class B extends A{ class Sub extends Super{... class A{ public Super f1(int a){... protected void f2(super a){... public Object f1(int a){... //errore: Object è ancestor di Super //Il tipo di ritorno deve essere un sottotipo di quello usato dal padre. Perché? public Super f1(long a){... //overloading: Cambia il tipo dei parametri public Super f1(int a, int b){... //overloading: Cambia il numero dei parametri public Sub f1(int a){... //overriding: Valido perchè Sub è figlia di Super private Sub f1(int a){... //errore: Non posso diminuire la visibilità quando faccio overriding //posso solo aumentarla protected void f2(sub a){... //overloading: anche se Sub è figlio di Super non stiamo ridefinendo f2 public void f2(super a){... //overriding: la visibilità passa da protected a public. Nessun problema. 14
Java: Overriding Esercizio: Creare una classe PrivateOverride con un metodo privato f(). Successivamente dichiarare la classe Derived sofoclasse di PrivateOverride avente metodo pubblico f(). Discutere il problema dell overriding del metodo privato f() di PrivateOverride da parte del metodo pubblico f() di Derived. 15
Java: Overriding public class PrivateOverride { private void f() { print("private f()"); public static void main(string[] args) { PrivateOverride po = new Derived(); po.f(); class Derived extends PrivateOverride { I metodi privai sono automaicamente di Ipo final pertanto non sarà possibile in nessun modo fare overriding di tali metodi. In questo esempio il programma stampa public void f() { print("public f()"); private f() Dichiarando la variabile po come Derived avremmo invece in output private f() 16
Java: Overriding Esercizio: Cosa stampa il seguente programma class Super { public int field = 0; public int getfield() { return field; Stampa sup.field = 0, sup.getfield() = 1 sub.field = 1, sub.getfield() = 1, sub.getsuperfield() = 0 class Sub extends Super { public int field = 1; public int getfield() { return field; public int getsuperfield() { return super.field; class FieldAccess { public static void main(string[] args) { Super sup = new Sub(); // Upcast System.out.println("sup.field = " + sup.field + ", sup.getfield() = " + sup.getfield()); Sub sub = new Sub(); System.out.println("sub.field = " + sub.field + ", sub.getfield() = " + sub.getfield() + ", sub.getsuperfield() = " + sub.getsuperfield()) ; 17
Java: Polimorfismo Richiamo di teoria: il polimorfismo è una proprietà del codice in grado di comportarsi diversamente in diversi contesi di esecuzione. Nella programmazione orientata agli ogger il polimorfismo è legato alle relazioni di eredità tra classi. L overriding appena visto rende possibile che gli ogger apparteneni a delle sofoclassi di una stessa classe rispondano diversamente alle stesse istruzioni. Il duck typing (che non esiste in Java ma esiste in linguaggi come Ruby e Python è un esempio estremo di polimorfismo. 18
Java: Polimorfismo Esercizio: Le Figure si dividono in Cerchi, QuadraI e Triangoli. Ogni figura mefe a disposizione i metodi draw() e erase() per rispervamente stampare e cancellare la figura. IpoIzzando che draw() e erase() stampino semplicemente delle stringhe che riportano l operazione che si sta eseguendo, modellizzare la gerarchia di classi che discende da Figura. 19
Java: Polimorfismo public class Shape { public void draw() { public void eraseo { Molto semplice, ci serve per svolgere i prossimi 2 esercizi. public class Circle extends Shape { public void draw() { System.out.println("Circle.draw()"); public void erase() {System.out.println("Circle.erase() ") ; public class Square extends Shape { public void draw() {System.out.println("Square.draw()"); public void erase() {System.out.println("Square.erase() ") ; public class Triangle extends Shape { public void draw() {System.out.println("Triangle.draw()"); public void erase() {System.out.println("Triangle.erase() ") ; 20
Java: Polimorfismo Esercizio: Creare una classe RandomShapeGenerator che mediante un metodo next() ritorni in modo random un istanza di Circle, Square o Triangolo. public class RandomShapeGenerator { private Random rand = new Random(47); public Shape next() { switch(rand.nextlnt (3)) { default: case 0: return new Circle(); case 1: return new Square(); case 2: return new Triangle(); UIlizziamo la classe Random offerta da Java, inizializzata con un opportuno seed. Andare a guardare la Java documentaion per ulteriori defagli. 21
Java: Polimorfismo Esercizio: UIlizzando la classe RandomShapeGenerator creare un metodo main che mi permefa di disegnare delle figure generate casualmente. public class Shapes { private static RandomShapeGenerator gen = new RandomShapeGenerator(); public static void main(string[] args) { Shape[] s = new Shape[9]; // Fill up the array with shapes: for(int i = 0 ; i < s.length; i++) s[i] = gen.next() ; // Make polymorphic method calls: for(shape shp : s) shp.draw(); 22
Java: Polimorfismo Esercizio: UIlizzando la classe RandomShapeGenerator creare un metodo main che mi permefa di disegnare delle figure generate casualmente. public class Shapes { private static RandomShapeGenerator gen = new RandomShapeGenerator(); public static void main(string[] args) { Shape[] s = new Shape[9]; // Fill up the array with shapes: for(int i = 0 ; i < s.length; i++) s[i] = gen.next() ; // Make polymorphic method calls: for(shape shp : s) shp.draw(); StaIcamente a livello di codice io ho deciso di disegnare delle figure, tufavia a run- Ime il metodo draw() cambia a seconda del Ipo dinamico che siamo considerando. L output di questo programma è impredicibile 23
Java: Polimorfismo Esercizio: Cosa stampa? class A{ void f1(){ System.out.println("f1InA()"); void f1(int a){ System.out.println("f1InA(int a)"); void f2(){ System.out.println("f2inA()"); class B extends A{ void f1(int b){ System.out.println("f1InB(int b)"); class D extends B{ void f1(){ System.out.println("f1InD()"); class C extends A{ void f1(char c){ System.out.println("f1InC(char c)"); public class Polimorphism { public static void main(string[] args){ A St_A Dy_D=new D(); B St_B Dy_C=new C(); B St_B Dy_D=new D(); St_A Dy_D.f1(); St_A Dy_D.f1(2); St_B Dy_C.f1('c'); St_B Dy_D.f2(); Stampa: f1ind() Stampa: f1inb(int b) Stampa: f2ina() B D A C 24
Java: Polimorfismo Esercizio: Cosa stampa? public class Polimorphism5 { public static void main(string[] args){ Scarpa s=new Scarpa(); s.stampa(); Scarpa g=new ScarpaDaGinnastica(); g.stampa(); class Scarpa{ static void stampa(){ System.out.println("Scarpa"); Stampa: Scarpa Scarpa Non è possibile fare overriding di un metodo staico. In altre parole Il binding dinamico dei metodi esiste solo per quelli non- staici. Se avessimo dichiarato g come ScarpaDaGinnasIca in quel caso il metodo stampa() avrebbe dato come output Scarpa da ginnasica class ScarpaDaGinnastica extends Scarpa{ static void stampa(){ System.out.println("Scarpa da ginnastica"); 25
Java: Visibilità Richiamo di teoria: Le keyword di visibilità si possono applicare alle classi, agli afribui e ai metodi. Una classe può essere: public: la classe è visibile a tur package- private: la classe è visibile solo all interno del suo package. E il valore di default quando non si specifica nessuna visibilità. AFribuI e metodi possono essere: public, private, protected, package- private Anche qui se viene omesso si intende package- private 26
Java: Visibilità La prima colonna ci dice che la classe può sempre accedere ai suoi membri indipendentemente dal livello di accesso. La seconda colonna ci dice i permessi relaivi alle classi dichiarate nello stesso package (indipendentemente dalla parantela) La terza colonna ci dice i permessi relaivi alle sofoclassi dichiarate in altri package. L ulima colonna ci dice i permessi del mondo esterno. 27
Java: Visibilità Esercizio: Visibile? package Prova; public class A { private int private_int = 1; int package_int = 2; protected int protected_int = 3; public int public_int = 4; private void privatemethod() {... void packagemethod(){... protected void protectedmethod(){... public void publicmethod(){... public static void main(string[] args) { A a = new A (); // Crea un oggetto di classe A a.privatemethod(); a.packagemethod(); a.protectedmethod(); a.publicmethod(); System.out.println("private_int: " + a.private_int); System.out.println("package_int: " + a.package_int); System.out.println("protected_int: " + a.protected_int); System.out.println("public_int: " + a.public_int); Nessun errore: Dall interno della classe posso sempre accedere a qualsiasi afributo/metodo, indipendentemente dalla sua visibilità 28
Java: Visibilità Esercizio: Visibile? package Prova; public class B { public static void main(string[] args) { A a = new A(); a.privatemethod(); a.packagemethod(); a.protectedmethod(); a.publicmethod(); Il metodo privato non è visibile da una classe esterna. System.out.println("private_int: " + a.private_int); System.out.println("package_int: " + a.package_int); System.out.println("protected_int: " + a.protected_int); System.out.println("public_int: " + a.public_int); L afributo privato non è visibile a una classe esterna. 29
Java: Visibilità package ProvaB; import Prova.*; public class C extends A { public static void main(string[] args) { A a = new A(); a.privatemethod(); a.packagemethod(); System.out.println("private_int: " + a.private_int); System.out.println("package_int: " + a.package_int); Sono fuori dalla classe e dal package a.publicmethod(); System.out.println("public_int " + a.public_int); a.protectedmethod(); System.out.println("protected_int: "+ a.protected_int); C c = new C(); c.protectedmethod(); System.out.println("protected_int: " + c. protected_int); E consenito ad una istanza di una classe figlia far riferimento ai metodi e alle proprietà implementate nella classe padre ma non è permesso accedere ad essi afraverso una istanza della classe padre. 30
Java: Visibilità Esercizio: Date le segueni classi public class O1 { private O2 o2=new O2(); public O2 geto2(){ return o2; public class O2 { public String nome; public void stampa(){ System.out.println(o2.nome); Posso modificare dall esterno lo stato di O1? 31
Java: Visibilità Soluzione: Certo che posso. Basta accedere ad o2 che è l unico afributo di O1 e modificarlo. public static void main(string[] args){ O1 o1=new O1(); O2 o2=o1.geto2(); o2.nome="ciao"; o1.stampa(); L inconveniente nasce dal fafo che O2 dichiara il suo afributo come pubblico. Una volta ofenuto un riferimento all afributo di O1 posso modificarlo e O1 vedrà queste modifiche. 32
Java: Interface Esercizio: Modellizzare in Java un applicazione in cui le Persone si suddividono in StudenI e Lavoratori Possono esistere Persone semplici, StudenI, Lavoratori, ma anche degli StudenILavoratori. 33
Java: Interface Prima (pessima) idea public class Persona{ String nome; String getnome(){ return nome; public class Studente extends Persona{ public class StudenteLavoratore extends Lavoratore{ int matricola; int getmatricola(){ return matricola; int matricola; int getmatricola(){ return matricola; In Java non esiste l ereditarietà mulipla, ovvero non è possibile avere più di una classe padre. Questo mi crea una serie di limitazioni (superabili). public class Lavoratore extends Persona{ float salario; float getsalario(){ return salario; Questa soluzione eredita il comportamento di Lavoratore ma mi costringe a ricopiare il codice di Studente. Decisamente poco pulito. 34
Java: Interface La soluzione è nell uilizzo delle interfacce. Java ammefe la possibilità di implementare interfacce muliple. Definiamo quindi public interface Persona { String getnome(); public interface Studente extends Persona { int getmatricola(); public interface Lavoratore extends Persona{ float getsalario(); public class PersonaImpl implements Persona{ private String nome; public PersonaImpl(String nome) { this.nome = nome; public String getnome() { return nome; Le interfacce rappresentano un contrafo tra classi. Chi implementa una determinata interfaccia dovrà fornire una implementazione del metodo dichiarato nell interfaccia stessa. In questo caso PersonaImpl mefe a disposizione il metodo getnome() che deve essere garanito in accordo con l interfaccia Persona. 35
Java: Interface public class StudenteLavoratore extends PersonaImpl implements Studente, Lavoratore { private int matricola; private float salario; public StudenteLavoratore(String nome, int matricola, float salario) { super(nome); this.matricola = matricola; this.salario = salario; public int getmatricola() { return matricola; public float getsalario() { return salario; Lo StudenteLavoratore è una Persona, quindi può invocare il suo costrufore passando il suo nome. Lo StudenteLavoratore fornisce i servizi garanii dalle interfacce che implementa: Studente, Lavoratore Anche in questo caso sarò costrefo a ricopiare buona parte dell implementazione di Studente dentro StudenteLavoratore. TuFavia qui è esplicitato sintarcamente in Java il fafo che Studente fornisce tur i servizi fornii da Studente e da Lavoratore. Molto più ordinato. 36