• Non ci sono risultati.

Stampare una Pila Stampare una Pila

N/A
N/A
Protected

Academic year: 2022

Condividi "Stampare una Pila Stampare una Pila"

Copied!
23
0
0

Testo completo

(1)

Stampare una Pila

Stampare una Pila

• Quando si stampa un Pila gli elementi appaiono nell’ordine inverso a quello di inserimento; inoltre la Pila si vuota.

• Supponiamo di avere introdotto nella Pila i valori 1, 2, 3 nell’ordine; per stampare la Pila bisogna accedere ad ogni elemento e poiché è accessibile solo la testa, per poter “vedere” gli altri elementi si devetoglierela testa. La testa è l’ultimo elemento inserito e perciò gli elementi appaiono in ordine inverso.

Stampare una Pila

1 3 2

stampa testa stampa testa stampa testa

3 2 1

Pila vuota

• Se vogliamo stampare gli elementi nello stesso ordine di inserimento, dobbiamo prendere un’altra Pila e

“rovesciare” quella iniziale e stampare la nuova Pila.

1 1

2

Stampare una Pila

1 3 2

1

1

2

3

3 2

3 2 1

(2)

Pila o Stack

Analogamente se vogliamo eseguire la ricerca di un elemento in una Pila: non ci sono assiomi di “ricerca elemento” tra gli assiomi dello Stack.

• È necessario quindi utilizzare una Pila di appoggio, estrarre gli elementi dalla Pila in cui eseguire la ricerca. Se la testa coincide con l’elemento cercato allora l’elemento è presente. Se la Pila iniziale si vuota l’elemento non è presente. Successivamente si inseriscono nella Pila iniziale gli elementi tolti.

Pila o Stack

• Esercizio. Si consideri una formula matematica; scrivere un algoritmo per verificare se le parentesi sono o no bilanciate.

Analisidel problema.

La formula

{a + (b-c) * [(a + b) - 7]}

ha parentesi bilanciate, mentre la formula {a + (b-c}-5)

non ha parentesi bilanciate, anche se il numero di tonde e graffe aperte coincide con il numero di quelle chiuse. Quindi non è sufficiente contarle.

Pila o Stack

• I soluzione.

• Consideriamo un array di caratteri in cui memorizzare le parentesi. Si esamina la formula un carattere alla volta e si inseriscono le parentesi (aperte) nell’array.

• Quando si incontra una parentesi chiusa si verifica se l’ultima parentesi è la sua corrispondente aperta.

• In questo caso si toglie dall’array la parentesi aperta e si prosegue, esaminando il successivo carattere della formula.

Pila o Stack

• Se alla fine della scansione della formula l’array è vuoto, allora la formula ha parentesi bilanciate.

• Se si incontra una parentesi che non corrisponde oppure se non si vuota l’array, significa che le parentesi non sono bilanciate (vd. Soluzionilab7).

• Si vede che la gestione dell’array è fatta con un indice che cresce quando si inseriscono le parentesi e cala quando si tolgono.

(3)

Pila o Stack

• Osservando gli inserimenti e le cancellazioni delle parentesi, si vede che si tratta della gestione di una Pila.

II soluzione.

• Consideriamo le due stringhe

String aperte = "{[(";

String chiuse = "}])";

Invece di memorizzare caratteri (le parentesi) memorizziamo gli indici 0, 1, 2 dei caratteri delle due stringhe e consideriamo una Pila di interi.

Pila o Stack

• Consideriamo un carattere della formula e vediamo se è un carattere della stringa aperte.

• Se risulta:

aperte.indexof(carattere) ≠≠ -1≠ si tratta di una parentesi aperta: possiamo memorizzare il numero corrispondente al valore aperte.indexof(carattere) nella Pila (e sarà uno di questi: 0, 1, 2).

Pila o Stack

• Se invece risulta:

aperte.indexof(carattere) ==== -1

• allora il carattere esaminato non è una parentesi aperta, ma sarà un altro carattere oppure una parentesi chiusa.

• Esaminiamo allora le parentesi chiuse. Se risulta:

chiuse.indexof(carattere) ≠≠ -1 ≠ allora il carattere è una parentesi chiusa.

Pila o Stack

• Guardiamo la testa della Pila: se la testa della Pila ha memorizzato un numero che corrisponde al valore

chiuse.indexof(carattere)

significa che è memorizzata “la parentesi aperta” corrispondente e pertanto si estrae la testa.

• Se non coincide la formula è errata.

• Se lo Stack si vuota allora le parentesi sono bilanciate.

(4)

Controllo di accesso

Controllo di accesso

• Nel linguaggio Java l’accesso a variabili, metodi e classi può essere (Par. 10.7):

• public

• private

• protected

• di pacchetto (nessuna specifica)

• public significa che l’accesso è possibile da tutte le classi;

• private significa che l’accesso è possibile solo all’interno della classe;

Controllo di accesso

• protected significa che l’accesso è possibile dalla classe e da tutte le sottoclassi, ed anche dalle classi dello stesso pacchetto (Arg. Avanzati 10.3);

• di pacchetto significa che l’accesso è possibile all’interno dello stesso pacchetto.

• Come pacchetto possiamo considerare la directory nella quale si trovano le classi: la classe di prova e altre classi. È una specifica buona per classi e metodi ma non per le variabili di istanza che è bene che siano sempre private.

Controllo di accesso

• In un pacchetto si inseriscono classi aventi la medesima funzionalità. La prima riga del file che contiene la classe deve iniziare con l’istruzione:

package nomePacchetto;

• Per utilizzare una classe di un pacchetto si può scrivere:

nomePacchetto.nomeClasse

• oppure

import nomePacchetto.nomeClasse;

• Noi non tratteremo la costruzione di pacchetti, ma sarà bene costruire delle cartelle con le classi che risolvono o gestiscono un particolare problema.

(5)

Variabili statiche o di classe

Variabili statiche o di classe

• Si può avere bisogno di utilizzare una variabile che non sia di un esemplare della classe ma che possa essere condivisa da tutti gli esemplari della classe.

• Esempio. Supponiamo di voler numerare in maniera progressiva i conti correnti in modo tale che ogni correntista abbia un numero di conto diverso; tale numero viene incrementato di uno per ogni nuovo conto attivato:

il primo correntista ha il numero 1, il secondo il numero 2, ecc.

Variabili statiche o di classe

• Modifichiamo la classe BankAccount. Questa nuova variabile, numeroConto, rappresenta il numero di correntisti e il suo valore è l’ultimo numero assegnato. Possiamo pensare di fare la seguente modifica:

public class BankAccount{

private int ultimoNumero;

private int numeroConto;

public BankAccount(){

ultimoNumero++;

numeroConto = ultimoNumero;

} . . . }

Variabili statiche o di classe

• Questo costruttore, però, non funziona perché la variabile ultimoNumero è una variabile di esemplare: ne esiste una copia per ogni oggetto; il risultato è che tutti i conti creati hanno il numero di conto uguale a 1.

Ci servirebbe una variabile condivisa da tutti gli oggetti della classe. Una variabile con questa semantica si ottiene con la dichiarazione static:

private static int ultimoNumero=0;

(6)

Variabili statiche o di classe

Una variabile static, detta anche variabile di classe, è condivisa da tutti gli oggetti della classe. Di essa ne esiste un’unica copia, ed è per questo che può incrementarsi effettivamente.

• Questa variabile è memorizzata al di fuori degli oggetti BA.

• Ogni metodo e costruttore della classe può accedere a questa variabile e modificarla.

Variabili statiche o di classe

• Per inizializzare una variabile statica non si può utilizzare una istruzione del costruttore, altrimenti il suo valore verrebbe inizializzato ogni volta che si costruisce un nuovo oggetto (e non sarebbe più condivisa).

• Pertanto si deve inizializzarla esplicitamente:

private static int ultimoNumero=0;

• oppure utilizzare l’inizializzazione di default.

• L’inizializzazione avviene una sola volta quando la classe viene attivata.

Variabili statiche o di classe

• Nella programmazione ad oggetti, l’utilizzo di variabili statiche deve essere limitato: infatti i metodi che usano variabili statiche hanno un comportamento che non dipende soltanto dai loro parametri, impliciti ed espliciti.

• Le variabili statiche devono essere private, per evitare accessi non controllati.

Variabili statiche o di classe

• È invece comune usare costanti statiche, come nella classe Math:

public static final double PI = 3.14159265358979323846;

Tali costanti sono di norma public e per ottenere il loro valore si usa il nome della classe seguito dal punto e dal nome della costante: Math.PI .

(7)

Vettori

Vettori

• In Java esistono classi per realizzare strutture di dati sequenziali con dimensione variabile, chiamate vettori. La classe ArrayList nella nuova versione permette di aggiungere alla definizione il tipo di dato di cui si vuole costruire il vettore (par. 7.2).

• La classe prevede metodi per operazioni di inserimento, rimozione, accesso, ecc.

• Sintassi.

ArrayList<tipoClasse> nomeoggetto = new ArrayList<tipoClasse>();

Vettori

• Metodi:

• add(elemento): aggiunge alla fine della sequenza un elemento di tipo <tipoClasse>

• add(i, elemento): inserisce un elemento nella posizione i

• get(i): restituisce l’elemento nella posizione i

• remove(i): toglie l’elemento nella posizione i

• set(i, nuovoelem): sostituisce nuovoelem nella posizione i

• size(): restituisce la dimensione del vettore.

• Per eseguire la ricerca di un elemento si effettua una scansione della struttura.

Vettori

• Le parentesi < … > indicano che tipoClasse è un tipo parametrico, vale a dire che si può inserire il nome di una classe che rappresenta un tipo qualunque di dato; per questo motivo la classe ArrayList viene anche detta “classe generica”.

• Costruiremo un array di oggetti di tipo BA e un vettore ArrayList di oggetti di tipo BA (vd.

Laboratorio 7).

(8)

Classi involucro

Classi involucro

La classe ArrayList accetta solo tipi di dato che rappresentano oggetti. Se vogliamo utilizzare ArrayList per gestire un tipo base come si può fare?

• Il linguaggio permette di “trasformare”

l’elemento del tipo base in un oggetto della classe corrispondente. Queste classi si chiamano: classi involucro.

Esistono classi involucro per ognuno dei tipi base: Byte, Short, Integer, Long, Float, Double, Character, Boolean.

Classi involucro

• Un esemplare di una classe involucro contiene un elemento del suo tipo base corrispondente.

• Per costruire un esemplare della classe involucro, nelle versioni precedenti si doveva esplicitamente eseguire la creazione dell’oggetto:

Double d = new Double(3.5);

nella versione attuale si scrive:

Double d = 3.5;

Classi involucro

• Per effettuare il viceversa, recuperare dall’oggetto il suo contenuto, nella versione precedente si doveva scrivere:

double x = d.doubleValue();

ora si scrive semplicemente:

double x = d;

• L’occupazione di spazio di x e d è diversa: x è uno scalare, d è un oggetto (un riferimento più l’area per l’oggetto).

(9)

Classi involucro

Stack Heap

x

d

3.5 3.5

Classi involucro

• Se si volesse costruire un vettore di numeri reali si dovrebbe scrivere:

ArrayList<Double> vettore = new ArrayList<Double>;

• Per assegnare i valori all’oggetto vettore di tipo ArrayList si avrà:

vettore.add(3.5);

• Per conoscere il valore presente nella posizione i

double x = vettore.get(i);

Classi involucro

Questa tenica di auto-impacchettamento (detta auto- boxing) non sempre propone conversioni “ovvie”;

l’assegnazione:

Double oggetto = 2.0;

è corretta, mentre non lo è:

Double oggetto = 2; //errato perché viene interpretata dal compilatore come:

Double oggetto =

new Integer(2);//errato

Classi involucro

Altre conversioni, invece, sono possibili anche se

“strane”; si possono ad esempio effettuare le seguenti operazioni aritmetiche:

Double elem;

elem = d+1;

• converte automaticamente d al suo valore numerico (3.5), aggiunge +1, inserisce il valore ottenuto nell’involucro elem.

• Se non è strettamente necessario, è meglio non usare le classi involucro, ma i tipi base.

(10)

Interfacce

Interfacce

• Vogliamo rappresentare una proprietà astratta, per esempio un TDA, senza preoccuparci della sua implementazione.

• Il linguaggio Java mette a disposizione il concetto di interfaccia.

• Tramite le interfacce è anche possibile effettuare il riutilizzo del codice.

(Capitolo 9)

Interfacce

• Abbiamo detto che uno Stack è un contenitore di informazioni per il quale valgono le seguenti operazioni:

• isEmpty : verifica di contenitore vuoto

• top : visione del primo elemento (testa)

• push : inserimento di un elemento in testa

• pop : estrazione della testa

• Abbiamo detto che lo Stack si può realizzare mediante:

• array

• lista concatenata

Interfacce

Le due realizzazioni dello Stack svolgono gli stessi compiti.

• Vogliamo trovare il modo per poter dichiarare quali sono le operazioni del TDA, senza pensare alla scelta della struttura di dati con la quale si costruirà lo Stack.

Definiamoil TDA in una interfaccia, dicendo cosafa.

Realizziamo il TDA in una classe, dicendo comesi fa.

(11)

Interfacce

Sintassi.

public interface NomeInterfaccia{

firma dei metodi }

public class NomeClasse implements NomeInterfaccia{

metodi dell’interfaccia:

codice relativo all’implementazione scelta }

Interfacce

Esempio.

Definizione dell’interfaccia Stack:

public interface Stack{

boolean isEmpty();

void push(Object elem);

void pop();

Oject top();

}

//si può anche definire //Object pop();

//in questo caso restituisce il valore della // testa

Interfacce

Costruzione dello Stack:

public class StackArray implements Stack{

//codice relativo ad uno Stack con //array

}

public class StackListC implements Stack{

//codice relativo ad uno Stack con //lista concatenata

}

Interfacce

• Realizzazione dello Stack interfaccia Stack

classi StackArray StackListC

(12)

Interfacce

• L’interfaccia è definita con accesso public, perché per sua natura deve poter essere implementata da una classe qualunque.

• I suoi metodi sono automaticamente public (default).

• Quando si realizza in una classeun metododi un’interfaccia, questo deve essere definito public.

Interfacce

• Differenze tra classe e interfaccia.

• Un’interfaccia:

• non ha variabili di istanza

• non ha costruttori

• tutti i suoi metodi sono astratti: del metodo c’è solo la firma, non c’è codice

• tutti i metodi sono automaticamente pubblici.

Interfacce

Una classe può estendere una sola altra classe superclasse

sottoclasse

Interfacce

Una classe può realizzare più interfacce.

• Ogni interfaccia realizza una proprietà e una classe può voler realizzare varie proprietà diverse tra loro.

• Vediamo di costruire una classe StackArray che realizza (implements) l’interfaccia Stack.

• La classe StackArray utilizza un array ridimensionabile di oggetti.

Dovremo considerare l’eccezione “Stack vuoto” per i metodi pop e top.

(13)

Interfaccia Stack e classe StackArray

public class StackArray implements Stack{

private Object vett[]; //array

private int sp ; //stack pointer public StackArray(){ //costruttore

vett = new Object[2];

sp = 0; }

/** assiomi dello Stack: isEmpty, push, pop, top */

public boolean isEmpty() {

/** verifica di lista vuota: O(1) */

if (sp==0) return true;

else return false;

}

Interfaccia Stack e classe StackArray

public void push(Object elem) {

/**inserimento in testa: in media O(1)*/

if ( sp==vett.length ) raddoppio();

vett[sp] = elem;

sp++; } public void pop() { /**estrae il primo: O(1) */

if ( sp > 0 ) // if (!isEmpty()) sp--;

else throw new

EmptyStackException("\nStack vuoto: sp = "

+ sp + "\nnon si puo' eseguire pop ");

}

Interfaccia Stack e classe StackArray

public Object top(){

/**visione della testa: O(1)*/

if (isEmpty())

throw new EmptyStackException ("\nStack vuoto: sp = "

+ sp + "\nnon si puo' eseguire top ");

else

return vett[sp-1];

}

/** metodi accessori allo stack */

Interfaccia Stack e classe StackArray

/** restituisce la testa e la toglie */

public Object topAndPop(){//Object pop() . . . }

private void raddoppio() { //raddoppio di vett . . . }

//sovrascrive toString() public String toString(){

return "nello Stack ci sono " + sp +" elementi";

}

}//fine classe StackArray

(14)

Costruire un’eccezione

Costruiamo l’eccezione per gestire la situazione

“Stack vuoto”:

EmptyStackException

Per costruire una nuova eccezione dobbiamo fare due scelte.

I scelta. Stabilire se l’eccezione estenderà le RuntimeException oppure estenderà Exception.

II scelta. Vogliamo utilizzare i costruttori dell’eccezione o gestire un messaggio personale che spieghi il lancio dell’eccezione.

Costruire un’eccezione

I scelta.

• Le eccezioni che estendono le RuntimeException non sono a controllo obbligatorio.

• Le eccezioni che estendono Exception sono a controllo obbligatorio e pertanto in tutti i metodi che sono coinvolti nel lancio di un’eccezione si deve scrivere nella firma:

throws EmptyStackException I metodi sono: pop, top, topAndPop, main.

Costruire un’eccezione

II scelta.

Utilizziamo costruttori e metodi di Exception:

public class EmptyStackException extends Exception { //corpo vuoto

}

• Con questa scelta la classe solamente eredita il comportamento (metodi e costruttori) di Exception:

abbiamo solo dato un nostro nome che ricorda il tipo di problema.

Costruire un’eccezione

• Nel nostro esempio la classe

EmptyStackException estende le eccezioni RuntimeException, dato che nell’intestazione non c’è throws; inoltre abbiamo potuto inserire un messaggio in cui visualizzare il valore di sp.

• Solitamente in una classe che costruisce un’eccezione si hanno due costruttori:

• uno senza parametri

• uno con parametro di tipo stringa che richiama il costruttore della superclasse

(15)

Costruire un’eccezione

public class EmptyStackException extends RuntimeException {

//costruttori

public EmptyStackException(){//primo //corpo vuoto

}

public EmptyStackException

(String messaggio){//secondo super(messaggio);

//richiama il metodo getMessage //della classe RuntimeException }

}

Realizzare Stack

Per vedere la realizzazione dell’interfaccia Stack costruiamo una classe di prova per la classe StackArray.

Possiamo definire due oggetti:

1) StackArray stA =

new StackArray();

è la solita costruzione di un oggetto di tipo NomeClasse

2) Stack st = new StackArray();

abbiamo una conversione di tipo tra classe e interfaccia (Par. 9.2).

Realizzare Stack

Nel secondo caso l’operatore new costruisce un oggetto di tipo StackArray che è una classe che implementa l’interfaccia Stack e restituisce un riferimento a un oggetto di quel tipo, che può essere assegnato a st solo perché StackArray implementa Stack (altrimenti il compilatore segnalerebbe errore).

Tramite il riferimento st possiamo usare tutti i metodi dell’interfaccia Stack; non possiamo usare topAndPopche è solo della classe StackArray.

Realizzare Stack

Per capire la potenza della definizione di TDA come interfaccia, supponiamo che qualcuno abbia progettato la seguente classe:

public class StackX implements Stack{

...

}

Anche senza sapere come sia realizzata StackX, possiamo usarne un esemplare mediante il suo comportamento astratto definito in Stack.

(16)

Realizzare Stack

Possiamo quindi usare un esemplare attraverso i suoi metodi: isEmpty, top, pop, push.

• Analogamente possiamo usare un esemplare di qualsiasi altra classe, StackY, che realizza Stack.

• Vediamo con un esempio. Costruiamo una classe di prova che costruisce uno Stack inserendo oggetti di tipo stringa e poi stampiamo lo Stack.

Realizzare Stack

Nella classe di prova possiamo scrivere:

Stack s = new StackX();

s.push("Pippo");

s.push("Pluto");

s.push("Paperino");

//stampa dello Stack while(!s.isEmpty()){

System.out.println(s.top());

s.pop();

}

ottenendo la costruzione dello Stack, la sua stampa e lo svuotamento dello Stack.

Conversioni di tipo fra sottoclasse e

superclasse

Conversioni di tipo fra sottoclasse e superclasse

• Abbiamo costruito una classe SavingsAccount come sottoclasse di BankAccount.

• Può esserci la necessità di memorizzare un riferimento di una sottoclasse in un riferimento a superclasse: è quello che abbiamo fatto nell’esempio precedente dove push ha come parametro un elemento di tipo Object e abbiamo passato una stringa per costruire lo Stack.

(17)

Conversioni di tipo fra sottoclasse e superclasse

• È possibile memorizzare un riferimento a un oggetto di tipo SavingsAccount in un riferimento di tipo BankAccount che è una superclasse.

• Analogamente è possibile memorizzare un qualsiasi riferimento a oggetto in un riferimento di tipo Object che è la superclasse universale.

• La conversione di tipo è automatica (Par.10.5).

Conversioni di tipo fra sottoclasse e superclasse

• Consideriamo questi riferimenti:

SavingsAccount sA =

new SavingsAccount(2);

BankSAccount bA = sA;

Object oA = sA;

• Abbiamo tre riferimenti diversi che vedono la stessa areadi memoria.

Conversioni di tipo fra sottoclasse e superclasse

sA

bA oA

balance 0

interestRate 2

Conversioni di tipo fra sottoclasse e superclasse

Ogni riferimento può usare solo i metodi della sua classe:

bA.deposit(500);

bA può modificare il saldo, però non può usare addInterest.

• Con il riferimento oA (di Object) si possono usare solo i metodi di Object (toString, equals, e pochi altri).

• Ciò nonostante può essere utile usare un riferimento della superclasse.

(18)

Conversioni di tipo fra sottoclasse e superclasse

Aggiungiamo a BankAccount il seguente metodo che permette di trasferire denaro da un conto ad un altro:

public class BankAccount{

. . .

public void transferTo

(BankAccount other, double amount){

this.withdraw(amount);

other.deposit(amount);

}

}//fine BA

Conversioni di tipo fra sottoclasse e superclasse

• Esempio.

BankAccount padre =

new BankAccount(5000);

BankAccount figlio = new BankAccount();

padre.transferTo(figlio, 300);

• Controlliamo con una stampa il saldo di entrambi:

Conversioni di tipo fra sottoclasse e superclasse

System.out.println

(padre.getBalance());//4700 System.out.println

(figlio.getBalance());//300

• Con l’invocazione del metodo

padre.transferTo(figlio, 300);

• abbiamo che:

this.withdraw(300); //this=padre other.deposit(300); //other=figlio

Conversioni di tipo fra sottoclasse e superclasse

Supponiamo ora che padre debba trasferire denaro ad un conto di tipo SavingsAccount:

SavingsAccount cliente =

new SavingsAccount(0.5);

• Possiamo fare:

padre.transferTo(cliente,200);

• L’assegnazione è corretta perché il metodo transferTosi aspetta di ricevere come parametro un riferimento a BA e invece riceve un riferimento a SA che è una sottoclasse: viene eseguita una conversione automatica.

(19)

Conversioni di tipo fra sottoclasse e superclasse

Il compilatore controlla solo che il riferimento di transferTose non è di tipo BA sia di tipo classe derivata da BA.

Avviene quindi la conversione:

BankAccount other cliente

• Ciò è analogo a quanto avviene quando si assegna ad una variabile reale un valore intero:

double a = 25;

• In realtà nei tipi base avviene una cosa diversa perché il valore 25 (32 bit in complemento a 2) viene memorizzato in una sequenza di 64 bit con mantissa, esponente: è comunque lo stesso principio.

Conversioni di tipo fra sottoclasse e superclasse

Il metodo transferTo invoca il metodo deposit:

public void transferTo(BankAccount other, double amount){

this.withdraw(amount);

other.deposit(amount);

}

Ma quale deposit?

Il parametro other contiene un riferimento di tipo SavingsAccount. Quindi sarà deposit di SavingsAccount: il metodo con cui si paga una tassa ad ogni versamento.

Polimorfismo

Polimorfismo

• Il termine polimorfismo deriva dal greco e significa “molte forme”.

• Nel trasferimento di denaro viene attivato:

• il metodo deposit di BA, quando il parametro è figlio

• il metodo deposit di SA, quando il parametro è cliente.

• La stessa operazione “versare denaro” viene eseguita in modi diversi che dipendono dal tipo dell’oggetto che viene effettivamente usato come parametro implicito.

(20)

Polimorfismo

Quando transferTo viene invocato, il riferimento passato a other “vede” un oggetto di tipo SA.

BA: other SA: cliente

Polimorfismo

• L’interprete Java sa che in other c’è un riferimento SA e quindi invoca deposit di SA.

Il compilatore può solo verificare la possibilitàche ciò possa avvenire.

• La scelta di quale sia effettivamente il riferimento viene fatta durante l’esecuzione del programma, vale a dire, solo quando il metodo viene invocato.

Polimorfismo

La scelta pertanto non è fatta in base al tipo del riferimento, other è definito di tipo BA, ma in base al tipo dell’oggetto che è relamente memorizzatoin other e che è di tipo SA.

• Il metodo ha lo stesso nome ma ha “forme diverse”.

Il metodo deposit di SA sovrascrive il metodo deposit di BA.

Sovraccarico e polimorfismo

Sovraccarico.

• Si parla di sovraccarico quando in una classe un metodo o un costruttore possiede parametri diversi;

ad esempio:

• in BankAccount abbiamo due costruttori, uno senza parametro e uno con parametro

• il metodo println possiede parametri diversi a seconda del tipo base.

• È il compilatoreche sceglie quale metodo invocare, prima che il programma venga eseguito.

• Si parla di selezione anticipata(early binding).

(21)

Sovraccarico e polimorfismo

Polimorfismo.

• Si parla di polimorfismo quando un metodo ha comportamenti diversi in relazione al tipo realmente memorizzato nel parametro implicito

• utilizzo di deposit di BA oppure di SA

• Èl’interprete JVMche decide durante l’esecuzione del programma quale metodo deve essere scelto.

• Si parla di selezione posticipata(late binding).

• Il compilatore controlla solo che il riferimento sia di tipo classe o sottoclasse.

Conversione inversa

Conversione inversa

• Si può fare la conversione inversa, vale a dire memorizzare un riferimento a superclasse in un riferimento a sottoclasse?

• È possibile fare una forzatura, cast, analogamente a quanto avviene per i tipi base.

La conversione però ha senso solo se nel riferimento a superclasse è effettivamente memorizzato un riferimento a sottoclasse; in caso contrario si ha un errore.

Conversione inversa

• Esempio.

SavingsAccount sA =

new SavingsAccount(2);

BankSAccount bA = sA;

SavingsAccount sA2 =

(SavingsAccount)bA; //cast

• Senza cast il compilatore segnala errore: tipi incompatibili.

• Se però bA (sul quale si fa il cast) non contiene un riferimento ad sA, durante l’esecuzione la JVM lancia l’eccezione ClassCastException.

(22)

Conversione inversa

• Per essere sicuri che un riferimento contenga un riferimento valido per quell’oggetto, si può usare l’operatore instanceof.

• Sintassi.

variabileoggetto instanceof Nomeclasse

• Esempio.

if(bA instanceof SavingsAccount) sA2 = (SavingsAccount)bA;

• L’operatore instanceof restituisce true, se la variabile è del tipo NomeClasse, false altrimenti.

TDA con numeri e oggetti

TDA con numeri e oggetti

• L’interfaccia Stack si riferisce ad elementi di tipo Object e così la sua realizzazione nella classe StackArray. Come primo esempio di Pila abbiamo visto una Pila di interi.

• È possibile “inserire” numeri interi in StackArray? Lo si può fare solo tramite le classi involucro: in tal modo si usa l’unica interfaccia Stack per rappresentare Pile di oggetti e di numeri, e si usano le sue realizzazioni con array e con lista concatenata.

TDA con numeri e oggetti

Queste strutture generiche, definite tramite Object, sono molto comode perché possono contenere dati di tipo qualsiasi. Sono però scomode se si esegue l’estrazione dell’elemento o se si effettua una ricerca nella struttura. Infatti viene restituito un riferimento a Object, anche se il riferimento contenuto è quello si una sottoclasse.

Pertanto è necessario effettuare un cast per ottenere un riferimento al tipo voluto.

Se non è strettamente necessario, quando si devono usare TDA sui tipi base, conviene costruire la realizzazionespecifica per il tipo base.

(23)

TDA con numeri e oggetti

Stack s = new StackArray();

Integer elem=5; //classe involucro s.push(elem);

System.out.println("testa " + s.top());

Integer x = (Integer)(s.top()); //cast int a = x;

System.out.println(a);

s.push(7); //direttamente System.out.println("nuova testa "

+ s.top());

a = (Integer)(s.top());

System.out.println(a);

Riferimenti

Documenti correlati

Ricoprendo il catodo in grafite con biossido di manganese la reazione di riduzione che avviene nella pila è quella sopra indicata dove non si ha evoluzione

• I livelli data link e fisico del modello OSI sono uniti in un unico “livello” network access, che non è un vero livello (secondo la definizione ISO-OSI) perché non ha una

Simbolo letto dall’ingresso (pu` o non leggere nulla) L’automa a pila passa alla configurazione successiva:. cambiando

Elenco delle eventuali pubblicazioni inerenti ai risultati di ricerca, con allegati fino ad un massimo di tre di questi documenti citati, con caratteristiche di brevità e sintesi

Tali altri gruppi, tuttavia, hanno cercato di risolvere il problema dal punto di vista teorico, sviluppando complesse formulazioni matematiche per simulare analiticamente il

Tesi di Laurea - Progetto di un ponte in calcestruzzo armato precompresso costruito per conci.

4 Sezioni e viste impalcato rampa entrata - scala 1:50 Sezioni travi, traversi e pile rampa entrata - scala 1:50 Piante e sezioni fondazioni rampa entrata - scala 1:50 Piante, viste

2) Scalpellatura e rimozione zone cls ammalorato in fase di rigonfiamento o distacco ove necessario, pulizia delle superfici per eliminare residui di polvere, bonifica delle