Come promesso iniziamo questa seconda puntata con la spiegazione riga per riga del codice citato nella precedente lezione (ved. questa pagina) cercando ci conseguire una visione un pò più generale circa il meccanismo di funzionamento di oggetti, classi e metodi.
I commenti
Cominciamo il nostro secondo appuntamento col fare una doverosa precisazione: quando si scrive un programma con un qualunque linguaggio di programmazione (non solo in Java…) è bene far uso dei commenti. Si tratta delle righe inserite da chi scrive il programma come ulteriore precisazione e spiegazione. L’uso dei commenti renderà il programma che si sviluppa più semplice da interpretare qualunque, in un secondo tempo, si dovessero effettuare delle modifiche.
Nel caso di Java i commenti sono le righe configurate nel modo seguente:
// commento su una riga
/** Commento su più righe
* Commento che si estende fino al carattere terminatore che vedete qui di fianco*/
I commenti vengono del tutto ignorati dal compilatore (ad essere precisi dall’analizzatore sintattico che ignora anche tutti gli spazi bianchi; ma per i nostri scopi questa puntualizzazione non è importante…) e di conseguenza non hanno alcun effetto sul programma. Rimane buona abitudine, tuttavia, far uso dei commenti per rendere il nostro codice più leggibile, anche nell’eventualità in cui questo debba essere letto da terze persone.
Analizziamo il semplice programmino presentato nella scorsa lezione. Esso si apre in questo modo:
In questo modo “comunichiamo” al compilatore Java che stiamo dichiarando una classe pubblica dal nome HelloWorld. La porzione di codice compresa tra le due successive parentesi graffe (detta blocco) è la definizione della classe. E’ bene sottolineare come la parentesi graffa che chiude il blocco di definizione della classe HelloWorld è quella che chiude l’intero listato del programma come dovrebbe risultare evidente dall’indentazione (si tratta del modo di disporre le righe nello schermo, facendole precedere da opportuni spazi per evidenziare le corrispondenze). Segue la riga:
Si tratta del primo metodo che scriviamo e che merita un approfondimento. Notiamo innanzitutto che il metodo si trova dentro la classe, in accordo con quanto evidenziato in precedenza. Il metodo main è un metodo particolare ed è l’unico metodo che l’interprete Java è in grado di eseguire autonomamente. Per questo motivo, ogni nostro progetto deve sempre contenere un metodo main che rappresenta il punto d’entrata del programma. Il metodo main deve iniziare esattamente come nell’esempio nel quale:
1. public
indica che è accessibile da qualunque punto del programma, quindi anche dall’interprete Java
2. static
indica che si tratta di un metodo di classe (ci occuperemo dei metodi di classe più avanti)
3. void
indica il valore di ritorno. Esso indica in realtà che il metodo non restituisce niente.
4. main
è il nome del metodo
5. all’interno delle parentesi abbiamo i parametri che vengono passati al metodo. Per ora non è importante capire l’esempio specifico: è sufficiente sapere che essi possono esserci o non esserci, così come possono essere in numero superiore ad uno. All’interno del corpo del metodo, anche qui racchiuso tra parentesi graffe, abbiamo l’unica vera istruzione del nostro esempio:
In questo modo, tramite la funzione di libreria standard println
scriviamo a video la stringa HelloWorld by ilsoftware.it. L’esecuzione del programma si presenta come in figura:
Classi e metodi da vicino
Come è fatta una classe
Ogni classe in Java può contenere un numero arbitrario di dichiarazioni di dati e di metodi. Le dichiarazioni (prive del modificatore static) dei dati, che normalmente si fanno precedere a quelle dei metodi, rappresentano i cosiddetti dati di istanza cioè i dati che ogni oggetto istanza di una certa classe conterrà. Supponiamo di voler scrivere una classe che simuli il comportamento di un dado, che dovrà essere soggetto a lanci casuali. Intuitivamente abbiamo:
public class dado
{
int valore;
...
//uno o più metodi
...
}
se noi creiamo un oggetto di tipo dado ci aspettiamo che in ogni momento si possa descrivere lo stato dell’oggetto in base al suo valore attuale, e infatti così è. Allo stesso modo, tuttavia, se creiamo due oggetti di tipo dado dalla classe precedente possiamo descrivere lo stato di ognuno in termini del suo dato valore, ovvero oggetto che creiamo da questa classe contiene un dato “valore”.
La dichiarazione dei dati si fa in genere seguire a quella dei metodi. Da un punto di vista progettuale quando ci occupiamo di scrivere i metodi di una classe non dovremmo mai perdere di vista il fatto che l’oggetto deve avere al suo interno tutto ciò che serve per essere considerato “stand-alone”. In parole povere, dovremmo considerare che le variabili di un oggetto dovrebbero essere modificate solo da parte di metodi dell’oggetto stesso. Questi accorgimenti servono per fare in modo che sia impossibile ad un oggetto qualunque di modificare, ad esempio, il valore del nostro dado. Questo concetto prende il nome di incapsulamento. In Java questo effetto si ottiene tramite una corretta progettazione e tramite i modificatori di visibilità, di cui parleremo più avanti. La dicitura del nostro esempio, in cui non vi è alcun modificatore, è da considerarsi corretta poiché in assenza di indicazioni specifiche da parte del programmatore, Java garantisce la “riservatezza” dei dati di istanza.
Come è fatto un metodo
Ogni dichiarazione di metodo che noi poniamo nelle nostre classi, specifica quali devono essere le operazioni da eseguire alla chiamata del metodo stesso. La definizione di un metodo richiede: il tipo del valore di ritorno, il nome del metodo e eventuali parametri in ingresso. Ad esempio, il metodo
void lancia()
{
valore = (int)(Math.random()*5)+1;
}
potrebbe far parte della classe dado. In questo caso il valore di ritorno è void, poiché il metodo non restituisce niente.
è il nome del metodo seguito da parentesi vuote (poiché non passiamo nessun paramentro al metodo). La riga posta all’interno del metodo significa “assegna alla variabile valore di questo oggetto un valore casuale (random) compreso tra 1 e 6”. Notate l’uso dell’operatore punto (.) per invocare il metodo Random (della libreria standard di Java): è questa la sintassi generale per l’invocazione dei metodi in Java. Inoltre dovete ricordarvi che, mentre il numero dei dati in ingresso può essere qualunque, in Java ogni metodo può restituire al massimo un valore, e lo fa con l’istruzione return
. Ad esempio il metodo
int getValue()
{
return valore;
}
potrebbe far parte ancora della classe dado così come
void setValue(int n)
{
valore = n;
}
In effetti questo è un modo di programmare diffuso ed efficiente, poiché rispecchia il concetto di incapsulamento e risulta, peraltro, particolarmente elegante. Notate come ci sia corrispondenza tra i dati che utilizziamo e i tipi che dichiariamo: valore
è dichiarato come dato di istanza e infatti il metodo getValue
restituisce un valore di tipo int (un intero); allo stesso modo il metodo setValue
prende in ingresso un intero e lo assegna alla variabile valore di quel determinato oggetto. Anche i nomi che sono stati assegnati ai metodi sono tutt’altro che casuali. Infatti nonostante il linguaggio non metta alcun vincolo, essi seguono delle convenzioni non scritte che si basano essenzialmente su regole di leggibilità: il nome getValue()
(“restituisci valore”) è molto più indicativo di a()
. In genere si tende inoltre ad evitare nomi eccessivamente lunghi e prolissi e a far cominciare il nome di un metodo con una lettera minuscola, al contrario delle eventuali parole successive che compongono il nome del metodo. I programmatori Java, normalmente, evitano di concatenare le parole con l’underscore (il trattino basso _ ) pratica molto diffusa presso i programmatori C/C++.
I costruttori
Quello che abbiamo appena presentato è la forma standard comune a tutti i metodi Java, ma esiste un’eccezione. Abbiamo detto continuamente fino ad ora che gli oggetti sono accessibili dopo che questi vengono creati. Ma come creiamo un oggetto a partire dalla sua classe?
Dobbiamo effettuare questa operazione tramite uno o più metodi speciali chiamati costruttori. Un costruttore è un metodo che deve obbligatoriamente avere lo stesso nome della classe (anche qui ricordatevi che Java è case-sensitive quindi attenzione alle maiuscole e minuscole) e non ha valore di ritorno mentre può avere o meno parametri in ingresso. La nostra classe dado potrebbe, ad esempio, avere i seguenti costruttori
public dado()
{
valore = 1; //valore arbitrario.
}
public dado(int n)
{
valore = n; //valore in ingresso.
}
L’uso della variabile n
in questo secondo caso deve far riflettere: essa non interferisce in nessun modo con l’omonima variabile del metodo setValue
poiché sono dichiarate in metodi differenti.
I costruttori vengono utilizzati tramite un particolare operatore riservato: new
.
La sintassi generale è:
TipoDato nomevariabile = new
costruttore;
ad esempio, nel nostro caso:
potremmo poi pensare a qualcosa del genere:
sei_facce.lancia();
System.out.println(sei_facce.getValue());
per lanciare il dado e visualizzare il valore che abbiamo ottenuto.
Potete scaricare da qui i file (sono due, da inserire in un unico progetto) utilizzati per questo esempio in versione integrale così da rendere facilmente comprensibile la struttura del nostro programma Java ed, in particolare, per il momento, della classe dado.