Per ogni classe (tipo di oggetti) sono definite
Abbiamo visto come definire classi con componenti.
Adesso vediamo come si definiscono classi con nuovi metodi.
È una operazione che e' possible fare con/sugli oggetti
Esempio: stampare una componente, cambiare una componente, ecc.
In generale metodi possono:
Un metodo può anche non avere qualcuna di queste parti.
L'elaborazione dati può riguardare calcoli (es. calcolo della distanza) oppure modifiche all'oggetto (es. cambiare le coordinate)
Bisogna specificare le sue parti:
Attenzione! quando dico "input"
e "output" intendo:
quello che entra ed esce dal metodo
Uso la metafora in cui il metodo è un dispositivo
Non viene stampato niente a meno che non si faccia println
Stampare tutte le componenti dell'oggetto
Esempio: aggiungiamo un metodo alla classe Studente
Classe di partenza:
class Studente { String nome; int media; }
Classe con nuovo metodo:
class Studente { String nome; int media; void stampaDati() { System.out.println(this.nome); System.out.println(this.media); } }
Spiegazione -->
Un metodo è una operazione
Posso effettuare l'operazione attraverso una sequenza di istruzioni
La specifica del metodo dice quali istruzioni effettuare
Parti del metodo di sopra:
Le altre parti servono per argomenti e risultato (più avanti)
Questo programma stampa il nome di uno studente:
class StudStampa { public static void main(String args[]) { Studente s; s=new Studente(); s.nome="Ciccio"; s.media=29; s.stampaDati(); s.media=s.media+1; } }
Una volta definito un metodo, si può invocare come fosse un metodo predefinito.
L'oggetto di invocazione è s, quindi vengono stampati di dati di s
Vengono eseguite le istruzioni
A parole: si interrompe l'esecuzione, e si ``salta'' alla prima istruzione del metodo.
Attenzione! Quando è finito, si riprende dal punto in cui si era lasciato.
Un metodo definisce una operazione complessa.
Quando si invoca il metodo, viene eseguita l'operazione.
Poi si contiuna con il programma principale.
Una volta definto il metodo, è come se fosse diventata una operazione elementare.
Paragone: per spiegare come si arriva alla citta' universitaria, definisco il "metodo":
come andare alla citta' universitaria: uscire dall'edificio girare a destra andare fino al semaforo attraversare la strada girare di nuovo a sinistra
Una volta che so come si fa questa operazione, posso costruire un programma piu' complesso:
1. andare alla citta' universitaria 2. andare in segreteria 3. fare la fila 4. chiedere il certificato
Una volta detto come si fa una operazione complessa, quando scrivo il programma posso semplicemente dire "fai questa operazione complessa"
Le ricette di cucina sono sequenze di operazioni.
Però non viene specificato tutto ogni volta.
Per esempio, in una ricetta può esserci la istruzione "preparare un pan di Spagna"
Questa non è una istruzione elementare: viene invece specificata in un'altra ricetta.
Ricetta del pan di Spagna=metodo
Ricetta della torta=programma che usa il metodo
Nella ricetta non viene ripetuto il procedimento per fare il pan di Spagna. Viene solo detto di farlo, e le istruzioni stanno in una altra ricetta.
In ogni caso, dopo aver fatto il pan di Spagna, si continua con la ricetta originaria.
Se la ricetta originaria è:
fare la crema al cioccolato fare il pan di Spagna tagliare il pan di Spagna farcire il pan di Spagna disegnare un ippopotamo
Quando si arriva alla seconda istruzione, si passa ad eseguire la sequenza per fare il pan di Spagna.
Finito di fare il pan di Spagna, si passa alla istruzione successiva (tagliare il pan di Spagna).
Il programma è una sequenza di istruzioni.
Il metodo è una sequenza di istruzioni che realizza una operazione precisa.
Quando si invoca il metodo, vengono eseguite le istruzioni corrispondenti, e poi si passa alla istruzione successiva del programma.
class Studente { String nome; int media; void stampaDati() { System.out.println(this.nome); System.out.println(this.media); } }
Il metodo stampaDati non ha argomenti, non produce un risultato e non modifica l'oggetto
Di solito i metodi hanno argomenti (move) oppure danno un risultato (getX, distance), oppure modificano l'oggetto (move)
Argomenti e risultato vengono specificati nella prima linea del metodo:
void = non ritorna niente stampaStudente = nome del metodo () = non ha argomenti
Quando si eseguono le istruzioni del metodo, this indica l'oggetto di invocazione.
Se nel programma faccio s.stampaDati()
quando eseguo il metodo, this è l'oggetto
puntato da s
Se nel programma faccio p.stampaDati()
questa volta this è l'oggetto p
Vediamo un esempio per volta
class Studente { ... void stampaDati() { System.out.println(this.nome); System.out.println(this.media); } }
Programma:
Studente s, q; ... q.stampaDati(); s.stampaDati();
Se nel metodo scrivessi s, non potrei stampare q, e viceversa.
Programma 1:
Studente s; int q;
Programma 2:
int s; Studente q;
Tutti e due i programmi possono usare la classe Studente!
Nella classe, non posso scrivere s oppure q, perchè il loro tipo dipende dal programma.
In generale: nel metodo della classe non posso usare una variabile che è dichiarata nel programma.
this dice: questo è l'oggetto su cui è stata fatta l'invocazione
Quando faccio s.stampaDato(), viene eseguito il metodo con this uguale a s.
Se faccio q.stampaDato() viene eseguito il metodo con this uguale a q
Scrivere una classe SPoint i cui oggetti rappresentano punti in uno spazio tridimensionale (tre variabili di instanza x ed y intere).
La classe ha in più un metodo che stampa la distanza del punto dall'origine.
Prima cosa, le variabili di istanza:
class SPoint { int x; int y; int z; ... }
Si tratta solo di prendere le istruzioni per stampare la distanza, e metterle dentro il metodo.
L'unica differenza (finora) è che le coordinate ora sono this.x, this.y e this.z.
class SPoint { int x; int y; int z; void stampaDistanza() { double d; d=Math.sqrt(this.x*this.x+this.y+this.y+this.z*this.z); System.out.println(d); } }
All'interno di un metodo, si possono anche dichiarare delle variabili.
Nella classe ci sono due righe simili:
int x; ... void stampaDistanza() { double d; ... }
Le due righe int x e double d sembrano entrambe dichiarazioni di variabili (una intera e l'altra reale).
Sono però due cose molto diverse:
class Studente { String nome; int media; void stampaDati() { System.out.println(this.nome); System.out.println(this.media); } }
Aggiungere un metodo che diminusce la media di uno.
Questo metodo non ha argomenti e valore di ritorno.
Dice come è fatta l'intestazione del metodo:
class Studente { String nome; int media; void diminusci() { ... } }
Modifica l'oggetto di invocazione
Se s è uno studente, allora nel programma farei s.media=s.media-1
Nel metodo, l'oggetto di invocazione viene chiamato this
class Studente { String nome; int media; void diminusci() { this.media=this.media-1; } }
class ProvaDim { public static void main(String args[]){ Studente p,t; p=new Studente(); t=new Studente(); p.nome="Ciccio"; p.media=18; t.nome="Alfonso"; t.media=21; t.diminuisci(); System.out.println(p.media); System.out.println(t.media); } }
Cosa viene stampato?
Il metodo diminusci viene applicato solo all'oggetto t
Quindi, p resta invariato
18 20
Mentre si esegue t.diminuisci(), la situazione è questa:
this indica dove si trova in memoria l'oggetto di invocazione
Per ora, basta sapere che lo stato della memoria è questo.
Scrivere un metodo setToOrigin da mettere nella classe SPoint, che pone il punto di invocazione nell'origine (tutte e tre le coordinate diventano 0)
class SPoint { int x; int y; int z; ... }
Il metodo non restituisce niente: void
Il nome è setToOrigin
Non ha argomenti: ()
class SPoint { int x; int y; void setToOrigin() { ... } }
L'intestazione si ottiene da queste tre cose: valore di ritorno, nome, argomenti.
Devo mettere a 0 le due variabili dell'oggetto di invocazione.
L'oggetto di invocazione è this
class SPoint { int x; int y; int z; void setToOrigin() { this.x=0; this.y=0; this.z=0; } }
Creo due punti, e su uno invoco il metodo.
class ProvaOrigin { public static void main(String args[]){ SPoint p, r; p=new SPoint(); r=new SPoint(); p.x=3 p.y=4; p.z=-3; r.x=-1 r.y=-1; r.z=-1; p.setToOrigin(); System.out.println(p.x); System.out.println(r.x); } }
Vengono creati due punti p ed r
Notare che non esiste nessuna variabile this finora!
Quando il programma arriva ad p.setToOrigin();, si eseguono le istruzioni di setToOrigin().
Solo ora viene creata la variabile this.
La variabile this viene creata automaticamente (non bisogna dichiararla) quando si esegue il metodo. Questa variabile indica l'oggetto di invocazione.
class ProvaOrigin { public static void main(String args[]){ SPoint p, r; p=new SPoint(); r=new SPoint(); p.x=3 p.y=4; p.z=-3; r.x=-1 r.y=-1; r.z=-1; p.setToOrigin(); r.setToOrigin(); System.out.println(p.x); System.out.println(r.x); } }
La prima volta che si invoca il metodo, è come prima.
Quando si invoca il metodo la seconda volta, è r.setToOrigin();. Quando si esegue il corpo del metodo, this è l'oggetto indicato da r.
Vengono messe a 0 anche le parti di r
Definire una classe RectPoint che ha le stesse variabili di istanza di Rectangle più una variabile centro di tipo Point. Definire un metodo setCentro che memorizza in questo punto il centro del rettangolo.
Sono le stesse del Rectangle più il punto.
import java.awt.*; class RectPoint { int x; int y; int height; int width; Point centro; }
Non ha valore di ritorno e nemmeno argomenti:
import java.awt.*; class RectPoint { int x; int y; int height; int width; Point centro; void setCentro() { ... } }
I valori del rettangolo sono this.x this.y this.width this.height this.centro
Usando i primo quattro si calcola il centro.
Le coordinate del centro vanno messe nel punto this.centro
import java.awt.*; class RectPoint { int x; int y; int height; int width; Point centro; void setCentro() { this.centro.x=this.x+this.width/2; this.centro.y=this.y+this.height/2; } }
Prima metto i valori del rettangolo, poi creo il punto e poi invoco il metodo
import java.awt.*; class ProvaRectPoint { public static void main(String args[]) { RectPoint r; r=new RectPoint(); r.x=23; r.y=12; r.height=4; r.width=5; // modifica solo le componenti // originarie x y width height r.centro=new Point(); r.setCentro(); System.out.println(r.centro); } }
Viene stampato il centro
Nota: l'oggetto punto va creato. Se non lo crea il metodo, lo deve creare il programma.
Vediamo come si usano quelli predefiniti.
Esempio: metodo move
Per usarlo, faccio p.move(10,20)
Metto prima l'oggetto di cui sto parlando, e fra parentesi i dati che servono per fare l'operazione.
Definiamo il metodo setToValue per SPoint, che modifica le coordinate.
È simile a setToOrigin, solo che i valori da mettere sono gli argomenti, e non 0
void setToOrigin() { this.x=0; this.y=0; this.z=0; }
Cosa devo cambiare:
void setToValue( ??? ) { this.x=??; this.y=??; this.z=??; }
Vorrei poter dire che il primo argomento è poi quello che va messo in this.x
Si usa un meccanismo simile alla dichiarazione di variabile:
void setToValue(int a, int b, int c) { this.x=a; this.y=b; this.z=c; }
La intestazione ha due funzioni:
Vediamo un programma di prova.
class ProvaMove { public static void main(String args[]){ SPoint p, r; p=new SPoint(); r=new SPoint(); p.setToValue(3,4,5); r.setToValue(-1,-1,-1); System.out.println(p.x); System.out.println(r.x); } }
Lo stato della memoria nella prima e seconda invocazione è differente.
p.setToValue(3,4,5);
Viene creato this, che diventa un riferimento all'oggetto puntato da p
Vengono create le tre variabili a b c
I valori dei tre argomenti vengono messi lí dentro.
Viene poi eseguito il corpo del metodo.
r.setToValue(-1,-1,2);
Questa volta, this indica il secondo oggetto.
Le variabili a b c vengono riempite con valori diversi.
Dall'esempio si capisce: il metodo non sa quali sono i valori degli argomenti.
Inoltre, ci possono essere due invocazioni con argomenti diversi.
Uso del metodo: scrivo
oggetto.nomemetodo(argomenti)
quando voglio usare un metodo.
Se ritorna un valore, viene scritto al posto
della invocazione
oggetto.nomemetodo(argomenti)
(è come se ci fosse il valore ottenuto
al posto di questi caratteri).
Definizione di metodo (ricorda il modo in cui si usa!)
tiporitorno nomemetodo(argomenti) { istruzioni return valore }
La parte return va omessa nel caso void (non si ritorna niente)
Definizione metodo: void prova(int x)
Invocazione metodo: oggetto.prova(10)
Nota: i primi sono valori, i secondi sono variabili!
Altra differenza:
i parametri formali di un metodo sono sempre gli stessi;
quelli attuali sono i valori che vengono passati,
quindi cambiano ad ogni invocazione.
(non finisce qui)
Variabile locale=variabile definita dentro un metodo.
void esempio(int x) { int y; ... }
Sia x che y sono variabili intere.
L'unica cosa che cambia è che in x viene inizialmente messo il valore con cui è stata fatta l'invocazione.
In entrambi i casi, la variabile non esiste più quando il metodo termina.
Quando scrivo la classe non so i valori dei parametri.
Che possono essere anche diversi in due invocazioni.
Uso dei contenitori per dire "questo è il valore passato".
Le variabili sono contenitori.
Creare un metodo della classe SPoint che stampa la distanza da un altro punto.
Deve quindi essere possibile fare:
SPoint p,t; ... p.stampaDistanza(t);
Osservazione: questo metodo può solo stampare.
È più comodo avere metodi che
calcolano qualcosa:
in questo modo posso usare il valore per
fare altri calcoli
Esempio: con il metodo sopra non posso calcolare il perimetro di un triangolo.
Il metodo getX() della classe Point è un metodo predefinito che ha un risultato (di tipo double)
Come si usa?
Quando scrivo p.getX() in una espressione, al suo posto viene messo il valore di p.x
Stessa cosa per distance
Quello che fanno è calcolare un valore.
Vediamo come scrivere nuovi metodi che fanno il calcolo di un valore.
class Studente { ... int getAnno() { return 2003; } }
Significato:
Quando si invoca un metodo:
Nel caso dell'esempio, il valore che viene ritornato è 2003
Mettere s.getAnno() all'interno di una espressione equivale a metterci 2003
Quindi x=(1+s.getAnno()+2)/3 equivale a x=(1+2003+2)/3
Perchà non posso direttamente scrivere 2003 nel programma, visto che tanto è la stessa cosa?
Il metodo calcola qualcosa in base ai valori degli argomenti e dell'oggetto
Quindi, non sempre so in anticipo il valore del risultato.
Questo metodo restituisce il valore del campo nome dell'oggetto di invocazione
class Studente { ... String getNome() { return this.nome; } }
Ora vediamo il significato delle parti
String getNome() { return this.nome; }
Il valore di ritorno è quello che il metodo comunica come risultato al programma.
Quando scrivo s.getNome(), dopo l'esecuzione delle istruzioni del metodo il valore ritornato viene messo al posto di s.getNome()
Come per tutti i metodi:
Student tizio; tizio=new Studente(); tizio.nome="Albert"; ... String s; s=tizio.getNome();
tizio e' l'oggetto di invocazione
Si esegue il corpo del metodo
return this.nome significa: il risultato è la stringa tizio.nome
In questo caso, è come scrivere "Albert" al posto di tizio.getNome()
Scrivere un metodo che calcola la differenza fra un numero intero e la media di uno studente.
Primo: cosa deve fare il metodo?
Secondo: in base a questo, specifico il metodo.
Cosa fa: dato un intero, calcola la differenza con la media.
Il tipo di argomento permette di scrivere l'intestazione
int diffMedia(int x) { ... }
Calcolo la differenza fra x e il campo media dell'oggetto
Quando arrivo ad eseguire il corpo del metodo, so che x contiene il numero e this punta all'oggetto di invocazione.
int diffMedia(int x) { int d; d=x-this.media; ... }
Il valore calcolato sta in d, quindi...
int diffMedia(int x) { int d; d=x-this.media; return d; }
Il valore dopo return deve essere dello stesso tipo di quello specificato all'inizio.
Questo metodo non va bene:
Scrivere un metodo della classe RectPoint che trova l'area del rettangolo.
Non ha argomenti (usa solo l'oggetto) e trova un intero.
import java.awt.*; class RectPoint { ... int getArea() { ... } }
Attenzione! Non ho detto che deve stampare l'area, ma che la deve restituire!
La base e l'altezza stanno nella parte width ed height dell'oggetto.
import java.awt.*; class RectPoint { ... int getArea() { int a; a=this.width*this.height; ... } }
Questo è il valore calcolato:
import java.awt.*; class RectPoint { ... int getArea() { int a; a=this.width*this.height; return a; } }
Si poteva anche fare:
return this.width*this.height;
Per esempio: calcolare l'area di un triangolo che ha la stessa base e altezza:
import java.awt.*; class ProvaRectPoint { public static void main(String args[]) { RectPoint r; r=new RectPoint(); r.x=23; r.y=12; r.height=4; r.width=5; int a; a=r.getArea()/2; System.out.println(a); } }
Se il metodo avesse fatto la stampa dell'area, non sarebbe poi stato possibile fare la divisione per due (bisognava di nuovo calcolare l'area)
Simulare lo stato della memoria quando si arriva ad eseguire le istruzioni del metodo.
Quando si definisce il metodo bisgna pensare:
quando lui arriva a questo punto, lo stato della memoria e' questo
A questo punto, devo trovare le istruzioni che permettono di calcolare il valore che va restituito
Scrivere un metodo nomeEData che restituisce la concatenazione di nome e data di nascita di uno studente, lasciando uno spazio in mezzo.
class Studente { String nome; String data; int media; ... }
Suggerimento: se s e q
sono due stringhe,
allora s.concat(q)
è la stringa ottenuta per concatenazione.
Ha un risultato, ma nessun argomento.
class Studente { String nome; String data; int media; String nomeEData() { ... } }
Devo fare la concatenazione di tre stringhe
class Studente { String nome; String data; int media; String nomeEData() { String r; r=this.nome.concat(" "); String q; q=r.concat(this.data); ... } }
Non serve la new
(eccezione sugli oggetti stringa)
La stringa che volevo sta in q
class Studente { String nome; String data; int media; String nomeEData() { String r; r=this.nome.concat(" "); String q; q=r.concat(this.data); return q; } }
Inserire nella classe RectPoint un metodo che trova la distanza tra il punto in alto a destra e un altro punto (dato come argomento).
Faccio vedere direttamente la soluzione
import java.awt.*; class RectPoint { ... double distanza(Point p) { double d; int xp, yp; xp=this.x+this.width; yp=this.y+this.height; d=Math.sqrt((xp-p.x)*(xp-p.x) + (yp-p.y)*(yp-p.y)); return d; } }
Esiste un metodo per calcolare la distanza.
Usare questo metodo
Però servono due punti, non un punto e due coordindate.
A partire da this.x e this.y, creo un punto.
import java.awt.*; class RectPoint { ... double distanza(Point p) { Point q; q=new Point(); q.move(this.x+this.width, this.y+this.height); return p.distance(q); } }
Esercitazione sulle classi: