Metodi per le nuove classi

Per ogni classe (tipo di oggetti) sono definite

  1. le componenti degli oggetti
  2. i metodi che si possono invocare

Abbiamo visto come definire classi con componenti.

Adesso vediamo come si definiscono classi con nuovi metodi.


Cosa è un metodo?

È 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)


Come si definisce un metodo

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


Metodo senza argomenti e risultato

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 -->


Nuovi metodi: principio

Un metodo è una operazione

Posso effettuare l'operazione attraverso una sequenza di istruzioni

La specifica del metodo dice quali istruzioni effettuare


Metodo: anatomia

Parti del metodo di sopra:

Le altre parti servono per argomenti e risultato (più avanti)


Nuovo metodo: esempio di programma

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


Cosa succede quando si invoca il metodo?

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.


Definire operazioni

Un metodo definisce una operazione complessa.

Quando si invoca il metodo, viene eseguita l'operazione.

Poi si contiuna con il programma principale.


Metodo=operazione complessa

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"


Libro di cucina

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.


Punto di ritorno

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).


Metodi e programmi

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.


Altre parti del metodo

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

this

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


Perchè this e non s?

Vediamo un esempio per volta


Più invocazioni nello stesso programma

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.


Più programmi che usano la classe

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

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


Esercizio

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.


Punto+distanza

Prima cosa, le variabili di istanza:

class SPoint {
  int x;
  int y;
  int z;

  ...
}

Soluzione

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.


Due dichiarazioni diverse!

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:

int x
significa che ogni oggetto ha una variabile intera di nome x. Se non creo nessun oggetto, non esiste nessuna variabile; se creo cento oggetti, ognuno ha una variabile x
double d
è una variabile dichiarata all'interno di un metodo; viene creata solo quando si invoca il metodo, e scompare quando il metodo è finito.

Annullare un esame

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.


Soluzione

Questo metodo non ha argomenti e valore di ritorno.

Dice come è fatta l'intestazione del metodo:

class Studente {
  String nome;
  int media;

  void diminusci() {
    ...
  }
}

Corpo del metodo

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;
  }
}

Cosa succede in questo caso?

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?


Cosa stampa

Il metodo diminusci viene applicato solo all'oggetto t

Quindi, p resta invariato

18
20

La memoria, quando si invoca il metodo

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.


Esercizio

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;

  ...
}

Primo passo: l'intestazione

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.


Secondo passo: il corpo del metodo

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;
  }
}

Terzo passo: il programma di prova

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);
  }
}

Cosa succede in memoria

Vengono creati due punti p ed r

Notare che non esiste nessuna variabile this finora!


Quando si esegue il metodo

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.


Se si invoca nuovamente 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();

    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


Esercizio

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.


Variabili di istanza

Sono le stesse del Rectangle più il punto.

import java.awt.*;

class RectPoint {
  int x;
  int y;
  int height;
  int width;
  Point centro;
}

Intestazione del metodo

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() {
    ...
  }
}

Soluzione: corpo del metodo

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;
  }
}

Soluzione: programma di prova

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.


Metodi con argomenti

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.


Definizione di metodi con argomenti

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:


Notazione per gli argomenti

  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;
  }

Cosa significa?

La intestazione ha due funzioni:


Invocazione di metodo, in memoria

  1. nuova variabile this, che punta all'oggetto di invocazione
  2. nuove variabili a b c che contengono i valori degli argomenti
  3. si esegue il corpo

Vediamo un programma di prova.


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.


Prima invocazione

    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.


Seconda invocazione

    r.setToValue(-1,-1,2);

Questa volta, this indica il secondo oggetto.

Le variabili a b c vengono riempite con valori diversi.


Perchè non si scrivono i valori nel metodo?

Dall'esempio si capisce: il metodo non sa quali sono i valori degli argomenti.

Inoltre, ci possono essere due invocazioni con argomenti diversi.


Struttura generale di un metodo

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)


Parametri attuali e formali

Definizione metodo: void prova(int x)

Invocazione metodo: oggetto.prova(10)

parametri attuali
i valori che vengono passati al metodo in una invocazione
parametri formali
le variabili in cui vengono messi i valori che sono stati passati al metodo

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)


Parametri formali/variabili locali

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.


Perchè i parametri formali sono variabili?

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.


Esercizio

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.


Metodi con risultato

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.


Un metodo con risultato

class Studente {
  ...

  int getAnno() {
    return 2003;
  }
}

Significato:

int
il metodo calcola un valore intero
return
il valore che viene effettivamente dato indietro al programma che ha invocato il metodo

Cosa succede?

Quando si invoca un metodo:

  1. si crea this e si fa puntare all'oggetto di invocazione

  2. si creano i parametri formali (che sono variabili) e ci si mettono i valori dei parametri attuali

  3. si esegue il corpo del metodo (le istruzioni)

  4. quando si arriva a return valore, il valore viene rimandato indietro al programma

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è return?

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.


Esempio più significativo

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


Significato delle parti

  String getNome() {
    return this.nome;
  }
String
tipo di dato che viene ritornato
this.nome
componente dell'oggetto di invocazione
return this.nome
ritorna al programma questo valore

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 si invoca il metodo

Come per tutti i metodi:

  Student tizio;
  tizio=new Studente();

  tizio.nome="Albert";
  ...

  String s;
  s=tizio.getNome();

Cosa succede quando si invoca il metodo

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()


Esercizio

Scrivere un metodo che calcola la differenza fra un numero intero e la media di uno studente.


Come si progettano i metodi

Primo: cosa deve fare il metodo?

Secondo: in base a questo, specifico il metodo.


Esempio: metodo che fa la differenza con la media

Cosa fa: dato un intero, calcola la differenza con la media.

Il tipo di argomento permette di scrivere l'intestazione

  int diffMedia(int x) {
    ...
  }

Seconda parte: scrivere il corpo

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;

    ...
  }

Terzo: il valore di ritorno

Il valore calcolato sta in d, quindi...

  int diffMedia(int x) {
    int d;

    d=x-this.media;
  
    return d;
  }

Nota sul tipo

Il valore dopo return deve essere dello stesso tipo di quello specificato all'inizio.

Questo metodo non va bene:


Altro esercizio

Scrivere un metodo della classe RectPoint che trova l'area del rettangolo.


Primo: intestazione

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!


Secondo: calcolo il valore

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;

Programma di esempio

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)


Progettazione dei metodi

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


Esercizio

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.


Primo: intestazione

Ha un risultato, ma nessun argomento.

class Studente {
  String nome; 
  String data;
  int media;

  String nomeEData() {
    ...
  }
}

Secondo: calcolo il risultato

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)


Terzo: valore di ritorno

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;
  }
}

Esercizio: un metodo con argomento

Inserire nella classe RectPoint un metodo che trova la distanza tra il punto in alto a destra e un altro punto (dato come argomento).


Soluzione

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;
  }
}

Modifica: usare distance

Esiste un metodo per calcolare la distanza.

Usare questo metodo


Soluzione con distance

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);
  }
}

In laboratorio

Esercitazione sulle classi:

  1. serve un file per ogni classe, più uno per il programma
  2. prima si compila la classe, e poi il programma
  3. si esegue solo il programma