Vediamo dei dettagli sul passaggio dei parametri.
Intuitivamente, sono dati ``trasmessi" dal programma al metodo.
Prime due: già viste con le variabili scalari.
Oggi: con gli oggetti.
Ogni metodo ha una sua zona di memoria in cui ci sono le variabili.
Queste zone non comprendono gli oggetti, che stanno da un'altra parte.
class Esempio { static void prova() { int x; Point q; q=new Point(); } public static void main(String args[]) { int a; Point p; p=new Point(); } }
Una zona per ogni metodo, più una zona comune a tutti per gli oggetti.
Mettendo insieme: nel parametro formale ci va a finire l'indirizzo dell'oggetto passato.
Quindi: una modifica all'oggetto risulta nel programma.
Cosa stampa questo programma?
import java.awt.*; class Modifica { static void azzera(int x, Point p) { x=0; p.x=0; p.move(0,0); } public static void main(String args[]) { int x=12; Point p; p=new Point(); p.move(10,20); azzera(x, p); System.out.println(x); System.out.println(p); } }
Il programma stampa...
Prima fare il diagramma.
Poi dare la risposta.
Esiste solo la zona delle variabili di main
La zona per azzera viene creata solo quando si invoca azzera
Viene creata la nuova zona in cui ci sono i parametri formali x e p e le variabili locali (in questo caso, nessuna)
Nei parametri formali vengono messi i valori passati.
Valori passati: valore di x, e valore di p
Il valore di p è l'indirizzo in cui si trova l'oggetto.
x=0 mette 0 nella variabile locale.
p.x=0; e p.move(0,0); modificano l'oggetto il cui indirizzo sta in p
Quando il metodo termina, viene eliminata la sua zona di memoria.
Quando passo una variabile, questa non viene modificata: x mantiene il valore 12
Quando passo un oggetto, questo può venire modificato p.x diventa 0
Non è questa la regola!
È una conseguenza della regola dei puntatori.
Definire più metodi con lo stesso nome.
Serve quando la stessa operazione si può fare con diversi tipi di dato.
Metodo: fare qualcosa sulla base di parametri.
Cosa succede se uso i parametri sbagliati?
import java.awt.*; class Errore { static void sbaglio(int x) { System.out.println(x); } public static void main(String args[]) { Point p; p=new Point(); p.move(12,-4); sbaglio(p); } }
Risposta: viene stampato questo errore:
Errore.java:13: sbaglio(int) in Errore - cannot be applied to (java.awt.Point) sbaglio(p); ^ 1 error
Errore.java:13: sbaglio(int) in Errore - cannot be applied to (java.awt.Point) sbaglio(p); ^ 1 error
L'errore è dovuto al fatto che il programma ha inviato un Point, ma il metodo si aspetta un int
Non ha importanza che poi un Point si possa anche stampare!
Nella invocazione, i parametri devono essere del tipo usato nella dichiarazione.
static void sbaglio(int x) ... sbaglio(...)
Fra le parentesi ci devo mettere un int
Perchè?
Il valore passato lo devo mettere nella variabile x che è intera.
Il parametro attuale va messo nel parametro formale.
I tipi devono poter permettere questo assegnamento.
Esempio: se il paramtro formale è double, posso passare un intero.
Se il parametro formale è intero, non posso passare un double, perchè l'assegnamento non si può fare.
System.out.println() è un metodo
Però funziona su tutti i tipi!
Intuitivamente: potrei dover fare le stesse cose su tipi diversi.
Come definire un metodo che lavora su più tipi diversi:
definire un metodo per ognuno dei tipi
Per disegnare un punto, servono le sue coordinate.
Disegno un rettangolo di larghezza e altezza zero.
public class UnPunto extends Applet { static void disegnaPunto(Graphics g, Point p) { g.drawRect(p.x,p.y,0,0); } public void paint(Graphics g) { ... } }
Se voglio disegnare un punto, devo per forza creare un punto.
public void paint(Graphics g) { Point p; p=new Point(); p.move(12,43); disegnaPunto(g, p); }
Alternativa: creo un metodo diverso:
static void disegnaPuntoDueInteri (Graphics g, int x, int y) { g.drawRect(x,y,0,0); } public void paint(Graphics g) { disegnaPuntoDueInteri(g, 12, 32); }
due metodi possono anche avere lo stesso nome, basta che il numero degli argomenti o il tipo di un argomento sia diverso
Dato che i due metodi per disegnare un punto hanno argomenti diversi, posso usare lo stesso nome.
Posso usare il nome disegnaPunto per tutti e due.
import java.awt.*; import java.applet.*; public class MetPunto extends Applet { static void disegnaPunto(Graphics g, Point p) { g.drawRect(p.x,p.y,0,0); } static void disegnaPunto(Graphics g, int x, int y) { g.drawRect(x,y,0,0); } static void paint(Graphics g) { Point p; p=new Point(); p.move(12,43); disegnaPunto(g, p); disegnaPunto(g, 12, 32); } }
Si può fare se il numero di argomenti è diverso.
Oppure se il tipo di almeno un argomento è diverso.
Qualche argomento può anche avere lo stesso tipo.
Il compilatore va a vedere il numero e il tipo dei parametri attuali (i valori che il programma manda al metodo)
Fra tutti i metodi con quel nome, sceglie quello che ha parametri di pari numero e tipo.
Non si possono definire due metodi che differiscono solo per il valore di ritorno.
// errore! int metodo() { ... } double metodo() { ... }
Definire dei metodi per calcolare l'area di un rettangolo, dati:
Se ho un rettangolo:
static int areaRect(Rectangle r) { return (r.width*r.height); }
Se ho due punti:
static int areaRect(Point min, Point max) { int w, h; w=max.x-min.x; h=max.y-min.y; return w*h; }
Attenzione! Bisogna sempre controllare che i due metodi si possano mettere insieme nello stesso programma.
Basta guardare le firme:
static int areaRect(Rectangle) static int areaRect(Point, Point)
Sia il numero che il tipo degli argomenti è diverso
Si possono usare i due metodi nello stesso programa,
anche se hanno lo stesso nome.
public static void main(String args[]) { Rectangle r; r=new Rectangle(); r.setBounds(10,10,20,20); System.out.println(areaRect(r)); Point p, q; p=new Point(); p.move(10,10); q=new Point(); q.move(30,30); System.out.println(areaRect(p, q)); } }
Le due invocazioni sembrano uguali, ma in effetti si parla di due metodi diversi!
I valori di ritorno dei metodi possono anche essere diversi.
class Prova { static int metodo(int x) { return 0; } static double metodo(double x) { return 0; } ... }
In generale: metodi sovraccarichi sono metodi diversi con lo stesso nome.
Dato che hanno lo stesso nome, per capire quale sto invocando, guardo il tipo/numero degli argomenti.
È per questo che esiste la regola che due metodi con lo stesso nome devono avere numero e/o tipo diverso di argomenti.
Definire un metodo che calcola la somma di due numeri.
I due numeri possono essere reali o interi (il valore di ritorno è di conseguenza)
Mi servono due metodi, uno per sommare interi e uno per sommare reali.
Le firme:
static int somma(int, int) static double somma(double, double)
I tipi dei valori di ritorno non hanno importanza.
I tipi degli argomenti si.
Sono diversi: si possono usare i due metodi.
Scrivo il corpo dei due metodi.
class DueSomme { static int somma(int x, int y) { return x+y; } static double somma(double x, double y) { return x+y; } public static void main(String args[]) { System.out.println(somma(12,23)); System.out.println(somma(12.3,23.1)); System.out.println(somma(12,23.1)); } }
Nota: posso anche usare gli stessi nomi per i parametri formali (x e y) nei due metodi (ogni metodo vede solo le sue variabili).
Ho un intero e un reale.
Viene invocato quello dei reali.
Regola generale: viene invocato quello che ha esattamente gli stessi tipi, se c'è, altrimenti quello che li può assegnare tutti ai parametri formali.
In questo caso: non sono due interi, per cui non posso usare il metodo con i due interi.
Posso assegnare 12 e 23.1 a due reali, per cui uso il metodo con due reali.
Si
Si: sono a tutti gli effetti due metodi diversi:
class Diversi { static void aaa(int x) { System.out.println("Questa e' una stringa"); } static void aaa(double x) { System.out.println(x+2); } public static void main(String args[]) { aaa(1); aaa((double) 1); } }
Non è però consigliabile farlo.
(il programma non si capisce più)
Si possono fare due metodi diversi se hanno numero di argomenti diverso.
Esempio: metodo che stampa la somma di interi, al quale posso passare da zero a tre interi.
Devo fare quattro metodi diversi.
class SommaInt { static int somma() { return 0; } static int somma(int x) { return x; } static int somma(int x, int y) { return x+y; } static int somma(int x, int y, int z) { return x+y+z; } public static void main(String args[]) { System.out.println(somma(2,3)); System.out.println(somma(2,3,4)); System.out.println(somma()); System.out.println(somma(2)); } }
Se i metodi fanno la stessa cosa conviene.
Quando si scrive il metodo, non devo usare un nome di metodo diverso a seconda degli argomenti.
Se i metodi non fanno la stessa cosa, è meglio dare nomi diversi.
Estendere la classe Point con un metodo vicino che vede se un punto è a distanza minore o uguale di uno dall'oggetto di invocazione.
Il metodo riceve come parametri un punto oppure due interi che rappresentano le sue coordinate.
Non è un metodo statico, ma un metodo della classe.
Il sovraccarico si può fare anche per questi metodi.
Devo fare un metodo che riceve un punto e un metodo che riceve due interi.
Devono però avere lo stesso nome e restituire tutti e due un booleano:
boolean vicino(Point) boolean vicino(int, int)
Dato che hanno parametri diversi, si può fare.
Il calcolo è fatto come al solito.
import java.awt.*; class NewPoint extends Point { boolean vicino(Point q) { return ( Math.sqrt((this.x-q.x)*(this.x-q.x)+ (this.y-q.y)*(this.y-q.y)) <=1); } boolean vicino(int x, int y) { return ( Math.sqrt((this.x-x)*(this.x-x)+ (this.y-y)*(this.y-y)) <=1); } }
Ogni volta che ho un oggetto, posso invocare un metodo della sua classe.
Questo vale anche dentro i metodi:
boolean vicino(Point q) { return (this.distance(q)<=1); }
NewPoint eredita il metodo distance da Point
Dato che this è un NewPoint, posso invocare il metodo.
Si può fare.
Se ho un oggetto, posso invocare tutti i metodi della classe (poi vedremo i modificatori di accesso).
import java.awt.*; class NewPoint extends Point { boolean vicino(Point q) { return (this.distance(q)<=1); } boolean vicino(int x, int y) { Point p; p=new Point(); p.move(x,y); return this.vicino(p); } }
Nel metodo vicino(Point) ho invocato il metodo distance (altro metodo di NewPoint)
Stessa cosa per vicino(int, int): ho invocato il metodo vicino(Point).
Pensate sempre che si tratti di un altro metodo, che però ha lo stesso nome.