Classi e Oggetti
In questo capitolo impareremo a definire le classi e quindi ad utilizzare gli oggetti. Abbiamo già detto più volte nei capitoli precedenti che un oggetto è un insieme di dati e funzioni che ne definiscono lo stato ed il comportamento. In Java le classi sono dei modelli per gli oggetti, ovvero sono delle strutture dati utilizzate per definire le caratteristiche e le funzionalità di un determinato tipo di oggetto. Una classe, una volta definita, viene instanziata creando un oggetto. E' importante non far confusione tra la definizione di una classe e l'instanzizione di un oggetto. Definire una classe consiste nell'elencare una serie di proprietà e funzioni, instanziare un oggetto invece vuol dire creare una variabile che presenta le caratteristiche della classe da cui è stata generata.
Potremmo affermare che una classe è la definizione di
un tipo di dato strutturato definito per rappresentare
un'entità complessa quale può essere ad
esempio un conto bancario, un prodotto commerciale, una
persona. Affrontiamo dunque lo studio delle classi Java
supponendo di aver bisogno di progettare una classe che
rappresenti un impiegato di una società.
Innanzitutto dobbiamo chiederci quali informazioni sono di
nostro interesse, per la definizione della classe che
chiameremo Impiegato. Trattandosi di un progetto a
fini didattici, semplificheremo il più possibile la
definizione della classe Impiegato, partendo proprio
dalle proprietà di nostro interesse. Supponiamo che un
impiegato sia caratterizzato dalle informazioni relative al
nome, cognome, anno di nascita, stipendio e posizione in
azienda. Cominciamo a tradurre quanto detto fino ad ora
secondo la sintassi Java.
public class Impiegato {
private String nome;
private String cognome;
private int annoNascita;
private int stipendio;
private String posizione;
final int stipendioMax;
final int stipendioMin; }
Procediamo con la spiegazione di quanto scritto. La prima
riga contiene la dichiarazione della classe. In essa compare
la parola chiave class che indica al compilatore Java
l'operazione che stiamo eseguendo. La classe
Impiegato è definita public, cioè
pubblica. Questo vuol dire che potrà essere utilizzata
o inclusa in altre classi o più in generale in altri
progetti, senza alcuna restrizione. Le parentesi graffe ({,
}) racchiudono il codice che definisce la classe.
All'interno compare la dichiarazione delle
proprietà che abbiamo considerato di nostro
interesse per la definizione di un oggetto di tipo Impiegato.
Tali proprietà sono il nome, cognome e posizione in
azienda rappresentate da tre variabili di tipo String
chiamate rispettivamente nome, cognome e
posizione. A queste si aggiungono le informazioni
riguardanti anno di nascita e stipendio, rappresentate da
altre due variabili di tipo int chiamate
rispettivamente annoNascita e stipendio. Infine
compare la dichiarazione di due costanti di tipo intero che
rappresentano rispettivamente lo stipendio minimo e massimo
che può ricevere un impiegato: stipendioMin e
stipendioMax. Osservate che nessuna di queste
variabile è stata inizializzata in questa fase di
definizione della classe.
Ogni dichiarazione di variabile contenuta nella classe
è composta [...].
[Pending: dichiarazioni variabili e parola chiave private]
Una volta definite le proprietà di una classe dobbiamo aggiungere una serie di funzioni, che in java vengono più propriamente detti metodi che operando sulle proprietà descrivendo il comportamento e le operazioni che sarà possibile effettuare sugli oggetti che verranno generati dalla classe. I metodi che siamo interessati a definire per la classe Impiegato sono riportati in tab.8.1 dove è descritta anche la funzione alla quale dovranno assolvere.
| motraNome() | visualizzerà nome e cognome dell'impiegato |
| calcolaStipendio() | restituirà un valore intero pari allo stipendio |
| calcolaEta(int anno) | restituirà un valore intero pari l'età dell'impiegato in particolare anno |
| variaStipendio(int variazione) | varia lo stipendio |
| aumentaStipendio(int aumento) | incrementerà lo stipendio di una quantità pari a aumento |
| riduciStipendio(int riduzione) | ridurrà lo stipendio di una quantità pari a riduzione |
Una volta completata la definizione della classe Impiegato sarà possibile instanziare diversi oggetti di tipo Impiegato, ognuno caratterizzato da un proprio stato. Col termine stato indichiamo l'insieme dei valori assegnati alle proprietà di ogni oggetto. Ad esempio instanzieremo un oggetto che rappresenti l'impiegato Mario Rossi, nato nel 1964, ... un secondo oggetto che rappresenta l'impiegato Luigi Verdi, nato nel 1970, ... e così via. Ad ognuno di questi oggetti, identificato da un nome esattamente come avviene per le variabili, sarà possibile applicare i metodi appena elencati, ed a seconda dell'oggetto a cui verranno applicati questi metodi otterremo risultati diversi, appunto, in base allo stato dell'oggetto in questione. Dovendo definire questa classe a fini didattici abbiamo inserito tre metodi che modificano la proprietà stipendio, mentre probabilmente ne sarebbe bastato uno. Questa scelta è stata fatta per evidenziare alcune proprietà dei metodi che verranno evidenziate nel corso di questo capitolo.
Cominciamo a scrivere il codice per la definizione dei metodi. Per definire il metodo mostraNome() scriveremo:
public void mostraNome()
{
System.out.println(nome + " " + cognome); }
Il metodo è costituito da un'intestazione e da un corpo racchiuso tra le parentesi graffe. Soffermiamoci ad osservare l'intestazione:
public void mostraNome()
in essa compare la visibilità del metodo
pubblic che definisce il metodo come pubblico,
cioè accessibile (visibile) dall'esterno della
classe. In poche parole questo vuol dire che una volta
instanziato l'oggetto, il metodo potrà essere
liberamente richiamato per operare sull'oggetto stesso.
Ai metodi pubblici si contrappongono quelli definiti
utilizzando la parola chiave private, i quali non
possono essere richiamati dall'esterno, ma possono essere
utilizzati solo all'interno della classe. Parleremo dei
metodi privati tra poco. La seconda parola chiave,
void, indica il tipo di dato restituito dal metodo al
termine dell'esecuzione. Generalmente un metodo esegue
un'elaborazione su dati immessi dall'esterno e/o
sulle proprietà dell'oggetto su cui viene
richiamato. L'elaborazione porta ad un risultato che
viene restituito come output. Questo valore può essere
di tipo intero (int), decimale (float o
double), stringa (String), o di un qualsiasi
altro tipo. In fase di progettazione di un metodo è
necessario dichiarare il tipo di valore che verrà
restituito al termine dell'esecuzione delle istruzioni
contenute nel metodo. Se il metodo non restituisce nessun
valore si usa la parola chiave void. Questo è
il nostro caso, infatti il metodo mostraNome()
visualizza nome e cognome dell'oggetto Impiegato
su cui è stato richiamato senza restituire alcun
valore. Infine, nella riga d'interstazione, compare il
nome del metodo, seguito da una coppia di parentesi tonde in
cui possono essere opzionalmente indicati i parametri
richiesti in input dal metodo. Nel caso di
mostraNome() nessun parametro di input è
richiesto.
Il corpo della funzione è costituito semplicemente
dall'istruzione:
System.out.println(nome + " " + cognome);
Istruzione già incontrata in precedenza, il cui scopo è visualizzare una stringa contenente il valore delle proprietà nome e cognome dell'oggetto di tipo Impiegato su cui è stato richiamato il metodo separate da uno spazio.
Passiamo a definire il secondo metodo della classe Impiegato, calcolaStipendio().
public int calcolaStipendio ()
{
return stipendio; }
Anche in questo caso vogliamo analizzare l'intestazione del metodo. Rispetto al metodo precedente in questo caso abbiamo dichiarato che il metodo restituisce un'informazione (output) sotto forma di numero intero. Questa dichiarazione viene rispettata nel corpo del metodo utilizzando la parola chiave return. return infatti, seguito da una variabile o da una costante, assolve proprio a questo compito. In questo caso dunque viene restituito, come output, il valore contenuto nella proprietà stipendio. E' importante assicurarsi che il tipo della variabile restituita coincida con il tipo del metodo dichiarato nell'intestazione. In questo caso il tutto risulta corretto in quanto abbiamo dichiarato che il metodo restituisce un valore di tipo intero (int), che è effettivamente anche il tipo della variabile stipendio.
Procediamo nella definizione del metodo calcolaEta().
public int calcolaEta (int annoCorrente)
{
int eta = annoCorrente - annoNascita;
if ((eta>=16) && (eta<=90))
return eta; else
return 0;
}
Nell'intestazione di questo metodo compare un'ulteriore differenza rispetto ai due metodi appena descritti. In questo caso abbiamo un metodo pubblico, che restituisce un valore di tipo intero e che richiede un parametro di input, anch'esso di tipo intero. Questo è quanto si deduce osservando l'intestazione del metodo. In particolare il tipo del parametro di input richiesto è indicato tra le parentesi che seguono il nome del metodo. Oltre al tipo viene indicato il nome che verrà attribuito al valore intero passato in input per farvi riferimento all'interno del corpo della funzione. Nel corpo della funzione viene dichiarata ed inizializzata una nuova variabile di tipo intero, chiamata eta. L'inizializzazione di questa variabile dipende dalla proprietà annoNascita dell'oggetto su cui viene richiamato il metodo e dal parametro annoCorrente passato come input. Per come è formulata l'istruzione di inizializzazione della variabile eta è ovvio che essa contiene l'età dell'impiegato. Segue un'istruzione if... then... else... il cui scopo è controllare che il parametro di input sia valido. Il controllo cosiste nel verificare che la variabile eta, a seguito dell'inizializzazione abbia assunto un valore compreso tra 16 e 90. L'età di 16 anni è la minima età lavorativa imposta per legge, per quanto riguarda il limite superiore si è invece supposto di non aver impiegati ultranovantenni. Se il controllo ha esito positivo viene restituito un numero intero rappresentate l'età dell'impiegato in questione, altrimenti viene segnalato l'errore tramite la restituzione di un'età pari a 0.
Definiamo il metodo variaStipendio().
private void variaStipendio (int variazione)
{
if (stipendio + variazione > stipendioMin)
stipendio = stipendio + variazione; else
stipendio = stipendioMin;
if (stipendio > stipendioMax)
stipendio = stipendioMax;
}
Dall'intestazione del metodo risulta che variaStipendio() è un metodo privato (definito tale dalla parola chiave private). Questa caratteristica fa si che una volta instanziato un oggetto di tipo Impiegato, non sarà possibile richiamare direttamente su di esso il metodo variaStipendio(). Un metodo privato può essere richiamato solo da altri metodi definiti internamente alla classe di appartenenza. Nel nostro caso il variaStipendio() verrà utilizzato dai metodi aumentaStipendio() e riduciStipendio(), per modificare la proprietà stipendio. Il metodo riceve in input un parametro intero, a cui viene attribuito il nome variazione, che può essere sia positivo che negativo. Nel corpo del metodo viene effettuato un primo controllo tramite un'istruzione if... then... else... sul parametro variazione al fine di verificare che una eventuale eccessiva riduzione dello stipendio non lo faccia risultare inferiore al valore stipendioMin che indica lo stipendio minimo percepito da un impiegato. Un secondo controllo invece assicura che lo stipendio, una volta registrata la variazione non superi la soglia massima definita tramite la costante stipendioMax.
Analizziamo i metodi aumentaStipendio() e riduciStipendio(). Osserviamo come questi facciano uso del metodo privato variaStipendio() per assolvere al proprio compito, quindi senza doversi preoccupare di eseguire alcun tipo di controllo sulle eventuali eccessive variazioni della proprietà stipendio.
public void aumentaStipendio (int incremento)
{
variaStipendio (incremento); }
Il metodo aumentaStipendio() è un metodo pubblico, non restituisce alcun valore ma ne richiede uno in input di tipo intero (int) cui viene dato il nome incremento. Il metodo opera sulla proprietà stipendio dell'oggetto richiamando variaStipendio().
Definiamo il metodo riduciStipendio().
public void riduciStipendio (int riduzione)
{
variaStipendio (riduzione*(-1)); }
Il metodo riduciStipendio() è un metodo pubblico, non restituisce alcun valore ma ne richiede uno in input di tipo intero (int) cui viene dato il nome riduzione. Il metodo opera sulla proprietà stipendio dell'oggetto su cui viene richiamato utilizzando il metodo variaStipendio().
Fino ad ora abbiamo definito proprietà e metodi della classe Impiegato. Siamo a buon punto, ma non abbiamo ancora concluso il nostro lavoro, infatti, se ben ricordate, al momento della dichiarazione delle proprietà, ci siamo limitati ad elencarle, senza preoccuparci di come queste verranno inizializzate. L'inizializzazione di un oggetto, al momento della sua creazione, avviene attraverso un metodo particolare detto costruttore. Il costruttore è un metodo che viene richiamato automaticamente ogni volta che viene creato un nuovo oggetto al fine di inizializzarlo. Il costruttore per essere distinto dagli altri metodi deve avere lo stesso nome della classe, quindi nel nostro caso, si deve chiamare Impiegato(). Definiamo il costruttore della classe Impiegato.
public Impiegato (String nome, String cognome, int annoNascita, String posizione)
{
stipendioMin = 750;
stipendioMax = 2000;
this.nome = nome;
this.cognome = cognome;
this.annoNascita = annoNascita;
stipendio = stipendioMin;
this.posizione = posizione; }
Analizzando l'intestazione del costruttore della classe Impiegato notiamo subito che si tratta di un metodo pubblico (public), che non è specificato alcun tipo di dato restituito. Tutti i costruttori devono avere queste due caratteristiche. Non è possibile definire costruttori di tipo private o che restituiscano un qualche tipo di valore in output. Il costruttore della classe Impiegato richiede in input quattro parametri: nome, cognome, anno di nascita e posizione del nuovo oggetto di tipo Impiegato che verrà generato. Osserviamo che ai parametri passati in input viene dato lo stesso nome delle proprietà della classe. Questa procedura, che altrimenti potrebbe generare confusione, viene risolta utilizzando la parola chiave this all'interno del corpo del costruttore. L'uso di this è un modo per far riferimento ad una proprietà dell'oggetto che stiamo generando. In questo modo, pur avendo entrambe lo stesso nome, non si farà confusione tra le variabili passate al costruttore e le corrispondenti proprietà dell'oggetto. Per evitare l'uso della parola chiave this avremmo potuto scrivere:
public void Impiegato (String n, String c, int a, String p)
{
stipendioMin = 750;
stipendioMax = 2000;
nome = n;
cognome = c;
annoNascita = a;
stipendio = stipendioMin;
posizione = p; }
All'inizio questo potrebbe sembrarvi il modo più semplice di procedere, tuttavia una volta appreso il funzionamento della parola chiave this, vi verrà più naturale scrivere i costruttori, ed in generale ogni altro metodo, come descritto nel primo esempio.
All'interno del corpo del costruttore possiamo richiedere l'esecuzione di qualsiasi tipo di operazione, tuttavia è buona norma limitarsi all'inizializzazione delle proprietà dell'oggetto.
A questo punto abbiamo decisamente completato la definizione della classe Impiegato! Per comodità vi riporto di seguito l'intero codice. Ho ordinato il codice secondo le convenzioni in uso tra i programmatori Java, ossia prima il costruttore, poi i metodi pubblici e privati, quindi le proprietà pubbliche, quelle private ed infine le costanti.
public class Impiegato {
public Impiegato (String nome, String cognome, int annoNascita, String posizione)
{
stipendioMin = 750;
stipendioMax = 2000;
this.nome = nome;
this.cognome = cognome;
this.annoNascita = annoNascita;
stipendio = stipendioMin;
this.posizione = posizione; }
public void mostraNome()
{
System.out.println(nome + " " + cognome); }
public int calcolaStipendio ()
{
return stipendio; }
public int calcolaEta (int annoCorrente)
{
int eta = annoCorrente - annoNascita;
if ((eta>=16) && (eta<=90))
return eta; else
return 0; }
public void aumentaStipendio (int incremento)
{
variaStipendio (incremento); }
public void riduciStipendio (int riduzione)
{
variaStipendio (riduzione*(-1)); }
private void variaStipendio (int variazione)
{
if (stipendio + variazione > stipendioMin)
stipendio = stipendio + variazione; else
stipendio = stipendioMin;
if (stipendio > stipendioMax)
stipendio = stipendioMax;
}
private String nome;
private String cognome;
private int annoNascita;
private int stipendio;
private String posizione;
final int stipendioMax;
final int stipendioMin; }
Ti potrebbe interessare anche
commenta la notizia
Chiedi alla nostra Redazione!