public - Ca` Foscari
Transcript
public - Ca` Foscari
DAIS – Univ. Ca' Foscari Venezia Programmazione ad oggetti Samuel Rota Bulò Interfacce ● ● ● ● ● ● Classi diverse possono condividere funzionalità comuni. – Es: classi che descrivono diverse forme geometriche possono avere in comune funzionalità quali calcolo dell'area o del perimetro – Es: classi che descrivono collezioni (liste, vettori, insiemi) Le interfacce sono tipi astratti che specificano solo firme di metodi tralasciando l'implementazione. Una classe che fornisce le implementazioni dei metodi può dichiarare di implementare l'interfaccia. Un'interfaccia, come nel caso delle Classi, viene dichiarata in un file .java e gli identificatori hanno per convenzione la prima lettera maiuscola. I metodi di un'interfaccia sono automaticamente pubblici. I metodi di un'interfaccia implementati da una classe devono essere dichiarati pubblici. public interface Shape { double getArea(); double getPerimeter(); } Implementazione di interfacce ● ● Una classe dichiara di implementare un'interfaccia attraverso la keyword implements. Una classe può implementare più interfacce contemporaneamente: – ● ● ● public class nomeClasse implements I1,I2,I3 { } La classe può implementare altri metodi oltre a quelli dell'interfaccia. Un'interfaccia può essere implementata da più classi Non possiamo istanziare direttamente un oggetto di tipo interfaccia. public class Rectangle implements Shape { ... public double getArea(){ return width*height; } public double getPerimeter(){ return 2*(width+height); } } public class Circle implements Shape { ... public double getArea(){ return r*r*Math.PI; } public double getPerimeter(){ return 2*Math.PI*r; } } Polimorfismo ● ● ● ● ● Un'interfaccia rappresenta tutte le classi che la implementano Un oggetto di una specifica classe può essere assegnato ad una variabile di tipo interfaccia se quell'interfaccia è implementata dalla classe di appartenenza. Assegnare una variabile di tipo interfaccia ad una classe invece non è possibile a meno di effettuare un esplicito casting di tipo. Quando è lecito un assegnamento da una variabile di tipo S ad una variabile di tipo T diciamo che S è sottotipo di T (S<:T) o che T è supertipo di S. Quando invochiamo il metodo di un oggetto attraverso una variabile di tipo interfaccia entra in gioco il polimorfismo: – Il metodo che verrà invocato effettivamente dipende dal tipo concreto dell'oggetto a cui la variabile si riferisce. – Avviene una ricerca dinamica del metodo da invocare a run-time. Tipo statico e dinamico ● ● ● ● Tipo di una variabile: – statico: tipo dichiarato – dinamico: il tipo dell'oggetto a cui la variabile riferisce. Il tipo dinamico può cambiare a run-time Il tipo dinamico di una variabile è sempre sottotipo del suo tipo statico Il casting (T) var causa un errore – A compile-time se T non è compatibile con il tipo statico di var – A run-time se T non è compatibile con il tipo dinamico di var. Viene generata un'eccezione ClassCastException. ● Per verificare se il tipo dinamico di una variabile var è sottotipo di T usiamo l'operatore instanceof: – var instanceof T – errore a compile-time se T e il tipo statico di var non sono in relazione di sottotipo/supertipo Esempio Shape anyShape=new Rectangle(0,0,1,1); Shape anotherShape=new Circle(2); Shape againAShape=new Stack(); // ERRORE ! Stack non implementa Shape System.out.println(anyShape.getArea()); //cosa stampa? System.out.println(anotherShape.getArea()); //cosa stampa? anotherShape=anyShape; System.out.println(anotherShape.getArea()); //cosa stampa? anotherShape=new Circle(1); Rectangle r=anyShape; // ERRORE ! Rectangle r2= (Rectangle) anyShape; // OK Rectangle r3= (Rectangle) anotherShape; //ERRORE! È un Circle ! System.out.println(r instanceof Shape) // true System.out.println(r instanceof Circle) //ERRORE System.out.println(anyShape instanceof Shape) // true System.out.println(anyShape instanceof Circle) // false Ereditarietà di interfacce ● Un'interfaccia può ereditare metodi da una o più interfacce con la keyword extends. public interface nomeInterfaccia extends I1,I2,I3{ //altri metodi } ● Se l'interfaccia J estende l'interfaccia I1 allora J è sottotipo di I1. public interface AreaComputable{ public double getArea(); } public interface PerimeterComputable{ public double getPerimeter(); } AreaComputable PerimeterComputable Shape public interface Shape extends AreaComputable, PerimeterComputable { } Cicli while(cond) { // istruzioni } ● ● Itera fintantoché cond è vera Condizione verificata all'inizio del ciclo for(expr1 ; cond ; expr2) { // istruzioni } ● ● ● Esegue expr1 all'inizio prima di iniziare a iterare Itera fintantoché cond è vera Alla fine di ogni ciclo esegue expr2 do { // istruzioni }while(cond); ● ● Itera fintantoché cond è vera Condizione verificata alla fine del ciclo for(nomeTipo var1: collection) { // istruzioni } ● ● Il tipo di collection deve essere sottotipo dell'interfaccia Iterable o di tipo array Itera sugli elementi dell'array/ iterable assegnandoli a var1 Cicli int[] a={5,4,3,2,1}; for(int i=0;i<a.length;++i) System.out.println(a[i]); for(int v:a) System.out.println(v); int i=0; while(i<a.length) { System.out.println(a[i]); ++i; } public interface Iterable<T> { Iterator<T> iterator(); } Approfondiremo i tipi parametrizzati più avanti. Nel caso delle strutture dati come per esempio ArrayList il tipo parametrico T rappresenta il tipo degli elementi della struttura e viene specificato in fase di dichiarazione ArrayList<int> list=new ArrayList<int>(); //ArrayList<int> implementa Iterable<int> l.add(1); l.add(2); for(int i:list) System.out.println(i); Java Collections Iterable<E> Collection<E> Set<E> List<E> TreeSet<E> ArrayList<E> HashSet<E> LinkedList<E> LinkedHash<E> ... ... Map<K,V> Queue<E> SortedMap<K,V> PriorityQueue<E> ... HashMap<K,V> LinkedHashMap<K,V> ... TreeMap<K,V> ... Wrappers ● ● A differenza degli array le collezioni possono contenere solo elementi di tipo riferimento. Se vogliamo creare collezioni di tipi primitivi dobbiamo ricorrere a delle classi wrapper che incapsulano il tipo primitivo. Tipo primitivo byte boolean char double float int long short Wrapper Byte Boolean Char Double Float Int Long Short Auto-boxing ● ● Da Java 5.0 la conversione tra tipi primitivo e i corrispondenti tipi wrapper è automatica. Attenzione: l'operatore di confronto == se applicato a due wrapper non confronta i valori incapsulati ma i riferimenti !! Double d=1.; int i=new Integer(2); d=2+d+i; System.out.println(d); // stampa 5.0 ArrayList<Double> list=new ArrayList<Double>(); list.add(9.); List.add(new Double(3)); ArrayList<double> list2=new ArrayList<double>(); list2.add(4.); list2.add(new Double(8)); System.out.println(1 == new Integer(1)); //true System.out.println(new Integer(1) == new Integer(1)); //false ! Tipi enumerazione ● I tipi enumerazione sono tipi che descrivono un insieme finito di valori costanti. public enum Day{ MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY } ● ● ● ● I tipi enumerazione possono essere utilizzati con il costrutto switch Una dichiarazione di enumerazione è una dichiarazione di classe a tutti gli effetti – può avere metodi e campi – non può avere costruttori pubblici – eredita da java.lang.Enum Le costanti che elenchiamo saranno le uniche istanze di questa classe. Devono essere elencate prima di tutto e terminare con ; se prevediamo di aggiungere altro dopo. Le costanti vengono create previa implicita invocazione di un costruttore sulla base dei parametri associati alla costante. Tipi enumerazione public enum Planet{ MERCURY (3.303e+23, 2.4397e6), VENUS (4.869e+24, 6.0518e6), EARTH (5.976e+24, 6.37814e6), MARS (6.421e+23, 3.3972e6), JUPITER (1.9e+27, 7.1492e7), SATURN (5.688e+26, 6.0268e7), URANUS (8.686e+25, 2.5559e7), NEPTUNE (1.024e+26, 2.4746e7); private final double mass; // in kilograms private final double radius; // in meters Planet(double mass, double radius) { this.mass = mass; this.radius = radius; } public double mass() { return mass; } public double radius() { return radius; } public static final double G = 6.67300E11; public double surfaceGravity() { return G * mass / (radius * radius); } public double surfaceWeight(double otherMass) { return otherMass * surfaceGravity(); } } Tipi enumerazione Planet p=Planet.MERCURY; switch(p) { case Planet.MERCURY: System.out.println(“Mercurio.”); break; case Planet.URANUS: System.out.println(“Uranio.”); break; default: System.out.println(“Altro pianeta.”); } System.out.println(p.mass()) // stampa 3.303e+23