Alcune Classi Standard
Object, Vettori
Esercizio dell’ultima volta
• Superclasse Persona
• Sottoclasse Libro
public class Persona
{ //OVERVIEW: una Persona e’ caratterizzata dal nome
//(una stringa) e dall’indirizzo (una stringa)
private String nome;
private String indirizzo;
public Persona(String nome,String indirizzo)
{//EFFECTS: costruisce una nuova Persona con nome
// nome ed indirizzo indirizzo
this.indirizzo=indirizzo;
this.nome=nome;
}
public String getNome()
{//EFFECTS: restituisce il nome di this
return this.nome;
}
public String getIndirizzo()
{//EFFECTS: restituisce l’indirizzo di this
this.indirizzo;
}
public String toString()
{//EFFECTS: restituisce una stringa che riporta nome ed
//indirizzo di this
return this.nome+this.indirizzo;}
public boolean equals(Persona p)
{//EFFECTS: restituisce true sse this e’ uguale a p
if (this.nome.equals(p.nome) &&
this.indirizzo.equals(p.indirizzo))
return true;
else
return false;
}
}
public class Studente extends Persona
{ //OVERVIEW: uno Studente e’ caratterizzato dal nome
// dall’indirizzo, e dal numero di matricola
private int mat;
public Studente(String n,String i, int mat)
{//EFFECTS: costruisce un nuovo Studente con nome
// n, indirizzo i e matricola mat
super(n,i);
this.mat=mat;}
public int getMatricola()
{//EFFECTS: restituisce la matricola di this
return this.mat;}
public String toString()
{//EFFECTS: restituisce una stringa che riporta nome,
//indirizzo e numero di matricola di this
return super. toString()+this.mat;}
public boolean equals(Studente p)
{//EFFECTS: restituisce true sse this e’ uguale a p
if
( super.equals (p) &&
this.mat==p.mat)
return true;
else
return false;
}
Implementazione I
public class Archivio
{ //OVERVIEW: un Archivio e’ un insieme di Persona
// senza ripetizioni di dimensione fissa
private Persona[] elements;
private int index;
public Archivio(int dim)
{//REQUIRES: dim >0
//EFFECTS: costruisce un nuovo Archivio vuoto
//di dimensione dim
elements=new Persona[dim];
index=0;}
Nell’implementazione garantiamo che:
1) 0<=index<elements.length
2) elements[i] non e’ null per 0<= i <index
3) elements[i] e’ null per index<= i <elements.length
Implementazione II
public String toString()
{//EFFECTS: restituisce una stringa che descrive l’insieme
//di persone contenute in this
String result=“”;
for ( int i=0; i< index; i++)
{result=result + elements[i].toString();}
return result; }
public String cercan(String indirizzo)
{//EFFECTS: restituisce il nome di una Persona con indirizzo
indirizzo che occorre in this, altrimenti restituisce la stringa vu
String result=“”;
for ( int i=0; i< index; i++)
{if
(elements[i].getIndirizzo().equals (indirizzo))
result=elements[i].getnome();}
return result; }
Implementazione III
public String cercai(String nome)
{//EFFECTS: restituisce l’indirizzo di una Persona con nome
nome che occorre in this, altrimenti restituisce la stringa vuota
String result=“”;
for ( int i=0; i< index; i++)
{if
(elements[i].getNome().equals (nome))
result=elements[i].getIndirizzo();}
return result; }
}
Implementazione: modificatori
public boolean insert(Persona p)
{//MODIFIES: this
//EFFECTS: se p non occorre gia’ in this e ci sono ancora
posizioni libere lo inserisce e restituisce true, altrimenti
restituisce false
for ( int i=0; i< index; i++)
{if
(elements[i].equals (p)) return false;}
if (index==elements.length)
return false;
elements[index]=p;
index++;
return true; }
Implementazione: modificatori
public boolean remove(Persona p)
{//MODIFIES: this
//EFFECTS: se p appartiene a this lo rimuove e
restituisce true, altrimenti restituisce false
for (int i=0; i< index; i++)
{if (elements[i].equals (p))
{if (i!=a.length-1)
{for (int j=i, j< index;j++)
elements[j]=elements[j+1];}
index--;
return true;}
}
return false;
}
Ereditarieta’
• Abbiamo visto come tramite l’ereditarieta’
e’ possibile estendere classi esistenti
-arricchendo lo stato degli oggetti
-aggiungendo nuove operazioni
•Se c1 estende c2, c1 e’ un sottotipo di c2
Classe Object
• La classe Object è la superclasse, diretta o indiretta, di
ciascuna classe in Java
• Object è supertipo di qualsiasi oggetto
Attenzione
• I tipi primitivi int, boolean, double non sono
sottotipi di Object, non sono oggetti (vedi la semantica di
FP)
• String e’ un tipo primitivo sottotipo di Object
• BankAccount, Persona, Libro sono esempi di
tipi non primitivi sottotipi di Object
A cosa serve?
Grazie al meccanismo dell'ereditarietà
 i suoi metodi sono ereditati da tutti i sottotipi
ad una variabile di tipo Object possiamo assegnare oggetti
di qualsiasi tipo (principio di sostituzione)
Esempio
Object obj;
String s=“io”;
Obj=s;
\\ e’ corretto un sottotipo e’ assegnato ad un supertipo
BankAccount b=new BankAccount();
Obj=b;
\\ e’ corretto un sottotipo e’ assegnato ad un supertipo
Obj=4;
\\ e’ un errore non e’ sottotipo
•Analogamente un metodo che ha un parametro formale Object potrebbe essere chiamato
con un parametro attuale del sottotipo
•Meccanismo essenziale per utilizzare lo stesso codice per i vari sottotipi
Metodi Eredidati da Object
•Sono metodi ereditati da tutti i sottotipi
•I metodi più utili sono:
{
\\EFFECTS: restituisce una rappresentazione
dell'oggetto this in forma di stringa. }
public String toString()
public boolean equals(Object obj)
\\EFFECTS :verifica se l'oggetto this è uguale a obj.
Commenti a toString()
•La definizione del metodo nella classe Object restituisce una stringa che contiene
il nome della classe dell'oggetto ed una rappresentazione esadecimale del codice hash
dell'oggetto (indirizzo in memoria dell'oggetto).
•Questo accade perché la classe Object non può conoscere la struttura dell'oggetto.
Il metodo ereditato e’ poco utile.
•Il metodo deve quindi essere sovrascritto in ogni classe che lo usa per ottenere
un risultato significativo. Tipicamente, di un oggetto si vogliono mostrare
(nella stringa restituita) i valori delle variabili d'istanza o comunque una
informazione significativa che descriva
lo stato interno
Commenti ad
equals
•Concettualmente, l'invocazione
<obj1>.equals(<obj2>)
del metodo equals dovrebbe restituire true quando il contenuto
dei due oggetti è uguale (non il riferimento, come per l'operatore ==).
•L'esempio tipico è il confronto tra stringhe.
•Il metodo equals della classe Object, e’ implementato
non potendo fare alcuna assunzione sulla struttura interna degli
oggetti su cui viene invocato (utilizza semplicemente l'operatore == per confrontarli.)
•Deve quindi essere sovrascritto in modo opportuno nel sottotipo (overriding)
a seconda delle caretteristiche degli oggetti
Object come supertipo
• Utilizzando
Object e’ possibile definire collezioni di dati
generiche
•Non hanno un tipo fissato, ma sono
in grado di memorizzare elementi di ogni sottotipo di
Object
•Simile al caso di Archivio, definito per memorizzare
oggetti di tipo Persona
•Puo’ essere usato per qualsiasi sottotipo, es. Studente
Un tipo di dato primitivo
•La classe java.util.Vector permette di definire degli oggetti
chiamati vettori (Vector)
-memorizzano sequenze di oggetti di lunghezza variabile
-possono contenere oggetti di tipo diverso, purche’ sottotipi
di Object
Tipo
Vector
Simili agli array a parte il fatto che
•
la dimensione di un vettore può variare durante
l'esecuzione di un programma
• non vanno creati per un tipo prefissato, gli elementi del
Vector hanno
un tipo generico Object
• quindi possono contenere oggetti di ogni tipo anche tra
loro disomogenei (tipo String o Integer) grazie al
principio di sostituzione
Tipo
Vector
• Vediamo la specifica
• Principali costruttori e metodi
• Per un uso piu’ avanzato rimandiamo ad LSD
Costruttore
public Vector (){
\\EFFECTS: crea un vettore vuoto}
• Notate che a differenza che per gli arrays non e’ necessario fissare al momento
della creazione la dimensione
• Ci sono anche altri costruttori tipo quelli degli arrays che permettono di creare
un vettore vuoto ma con una certa capacita’ (dato numero di posizioni allocate
ma vuote). Serve solo per avere implementazioni piu’ o meno efficienti (per ora
lo ignoriamo)
Metodi simili a quelli dell’array
public int size (){
\\EFFECTS: restituisce il numero di elementi presenti nel
vettore}
public Object elementAt (int index){
\\EFFECTS: restituisce l'elemento di indice index }
public void setElementAt (Object obj, int index){
\\MODIFIES: this
\\EFFECTS:
sostituisce obj all'oggetto della posizione index}
•
Se index e’ fuori dal size del vettore viene sollevata una eccezione
come per gli arrays
Metodi per aggiungere
public void insertElementAt (Object obj, int index){
\\MODIFIES:this
\\EFFECTS: inserisce obj nella posizione index e sposta tutti gli
elementi, da index in poi, di una posizione}
public void addElement (Object obj){
\\MODIFIES:this
\\EFFECTS: aggiunge una posizione alla fine che contiene obj }
• La dimensione del Vector cambia, viene aggiunta una posizione
alla fine o in un dato punto
Metodi per rimuovere
public void removeElementAt (int index){
\\MODIFIES:this
\\EFFECTS: rimuove l'oggetto presente nella posizione index e sposta
all'indietro di una posizione tutti gli elementi successivi
a quello rimosso}
public boolean removeElement (Object obj){
\\MODIFIES:this
\\EFFECTS: rimuove la prima occorrenza
dell'oggetto obj se presente restituendo true,oppure
restituisce false}
•
La dimensione del Vector cambia, viene ricompattato (non rimane una
posizione vuota)
Esempio: creazione di un Vector
Vector v=new Vector();
v.addElement(“a”); \\
v.addElement(“b”); \\
v.addElement(“c”); \\
v.removeElementAt(1);
\\ [] inizialmente vuoto
[a]
[a,b]
[a,b,c]
\\ [a,c]
Come leggere gli elementi?
Vector v=new Vector();
\\ []
v.addElement(“a”); \\ [a]
v.addElement(“b”); \\ [a,b]
v.addElement(“c”); \\ [a,b,c]
String s= v.elementAt(0); \\ e’ corretto?
•
Sembrerebbe, dovrebbe restituire una stringa, ma……..la specifica del metodo
public Object elementAt (int index){
\\EFFECTS: restituisce l'elemento di indice index }
Problema:errore di compilazione
• Stiamo usando un tipo Vector per memorizzare stringhe
• il compilatore non puo’ sapere quale tipo di valori sono
correntemente memorizzati nel Vector (il suo tipo
effettivo)
•
il compilatore conosce solo il tipo restituito dal metodo (il
suo tipo apparente, Object)
Necessario: Cast
Vector v=new Vector();
\\ []
v.addElement(“a”); \\ [a]
v.addElement(“b”); \\ [a,b]
v.addElement(“c”); \\ [a,b,c]
String s= ((String) v.elementAt(0));
\\ compila
•Il compilatore non riporta errori in quanto String e’ sottotipo del tipo
apparente Object
•Il Cast potrebbe provocare un errore a run-time qualora il valore
restituito dal metodo non fosse sottotipo di String (non in questo caso
ovviamente!)
Tipi Primitivi?
Vector elements=new Vector();
elements.addElement(3);
errore di tipo!
non e’ sottotipo di Object (il metodo addElement ha
parametro Object)
• int
• Analoghi problemi per gli altri tipi boolean,
double…
La soluzione
• Ogni tipo primitivo ha un corrispondente sottotipo
di Object, sono oggetti che memorizzano i valori
corrispondenti
• Per esempio, Integer e’ la classe involucro di int
• Ogni Integer e’ un oggetto che memorizza un
valore int
•
Integer e’ sottotipo di Object
• Classi analoghe per tutti gli altri tipi primitivi
•Integer ha (oltre ai soliti metodi toString e equals):
•un costruttore con parametro di tipo primitivo
public Integer(int value){
\\EFFECTS: crea un Integer che contiene il valore value}
•un metodo che produce il valore di tipo primitivo corrispondente
public int intValue(){
\\EFFECTS: restituisce il numero intero
contenuto in this}
Esempio
•Vogliamo memorizzare il valore 3 in un Vector
Integer e= new Integer(3); //creiamo l’Integer
elements.addElement(e); //lo inseriamo
•Vogliamo leggere un valore da un Vector di Integer e
trasformarlo in int
Integer i= (Integer) elements.elementAt(3); //Cast
int x= i.intValue();//conversione
Strutture Dati
•Il tipo Vector e’ utile per realizzare strutture dati
di dimensione variabile
•Esempio: pila
•Collezione di elementi organizzati in modo LIFO
•Operazioni per inserire e rimuovere (top, pop e push)
•Facciamo vedere specifica di una pila di interi
public class Stack {
\\OVERVIEW: uno Stack e’ una collezione di elementi (interi) organizzati per
\\ordine di inserimento con una politica LIFO. E’ modificabile
public Stack(){
\\EFFECTS: costruisce un nuovo Stack vuoto}
public boolean isEmpty() {
\\EFFECTS: se this e’ vuoto restituisce true, altrimenti false}
public int top() {
\\ REQUIRES:this non vuoto
\\ EFFECTS: ritorna l’ultimo elemento inserito}
public void pop() {
\\ REQUIRES:this non vuoto
\\ MODIFIES: this
\\ EFFECTS: rimuove l’ultimo elemento inserito}
public void push (int o){
\\ MODIFIES: this
\\ EFFECTS: inserisce o nella pila al top}
public String toString (){
\\ EFFECTS: restituisce una Stringa che descrive gli elementi di this, dal primo
inserito all’ultimo}
}
Esercizio I
• Implementare la classe Stack
• Usando una variabile d’istanza privata di tipo
Vector
• Il Vector sara’ vuoto al momento della creazione
della Pila
• Gli elementi dovranno essere inseriti in modo da
implementare la disciplina LIFO (non a caso)
• Per esempio l’ultimo elemento del Vector e’ il top
(ultimo inserito)
Astrazione tramite specifica
• Frammento di codice che opera sul tipo di
dato Stack
• Scritto in base alla specifica (interfaccia)
• E’ indipendente dalla implementazione
• Non puo’ accedere al Vector che
implementa la pila
public class UsaPila{
public static int somma(Stack p){
\\ REQUIRES: p non e’ null
\\EFFECTS: restituisce la somma degli elementi
\\di p}
public static boolean cerca(Stack p, int i){
\\ REQUIRES: p non e’ null
\\EFFECTS: restituisce true se i appartiene a p,false
\\altrimenti}
}
Nota
• I metodi statici sono difficili perche’ non
abbiamo un modo per accedere a tutti gli
elementi della pila (solo al top)
• Per contro accedendo al vettore che la
implementa (deve essere evitato) sarebbe
banale
• La pila passata per parametro non deve
essere distrutta (svuotata), per riferimento
Parametro per riferimento
Stack p1=new Stack(); []
p1.push[10];
[10]
p1.push[20];
[10,20]
p1.push[2];
[10,20,2]
int result= UsaPila.somma(p1);
Viene associato al parametro attuale p il
riferimento all’indirizzo di p1 (se il
corpo del metodo modifica p modifica
l’oggetto condiviso
Sviluppare un Tester
•
•
•
•
Prende dei valori da console
Costruisce una pila che li contiene
Testa i vari metodi, chiamandoli sul vettore
Per “vedere” il contenuto di una pila usare
toString
Scarica

Lucidi