Soluzione
Esercizio 004 - Uso delle costanti in Java
L'esercizio
riguardava l'uso dei modificatori static
final
usati come costanti in Java. Rivediamolo e commentiamolo.
La
seguente classe dichiara due costanti A e B inizializzate rispettivamente
ai valori 1 e 2.
// Costanti.java
class Costanti
{
static
final int A = 1;
static
final int B;
static
{
B = 2;
}
}
La
seguente classe Main usa le due costanti precedentemente dichiarate.
// Main.java
public class
Main {
static
public void main(String[]args) {
System.out.println("A="+Costanti.A+
" B="+Costanti.B);
}
}
Compilando
ed eseguendo le due classi abbiamo infatti la stampa di 1 e 2:
prompt> javac Costanti.java Main.java
prompt> java Main
A=1 B=2
prompt>
Se
però cambiamo il valore delle due costanti:
// Costanti.java
class Costanti
{
static
final int A = 3;
static
final int B;
static
{
B = 4;
}
}
e ricompiliamo
la classe modificata e rieseguiamo il Main cosa verrà stampato?
prompt> javac Costanti.java
prompt> java Main
A=1 B=4
prompt>
Ecco
qui la prima sorpresa! Il programma stampa 1 e 4 invece che 3 e 4.
Ma
come? E' corretto 1 e 4? Oppure ha sbagliato il nostro compilatore/interprete
Java preferito?
Calma
Calma! E' tutto perfettamente corretto.
Quando
la prima volta abbiamo compilato Main.java
per ottenere Main.class
il nostro compilatore ha analizzato il file Costanti.class
per capire cosa fossero le espressioni Costanti.A
e Costanti.B
che comparivano dentro la System.out.println.
Analizzando questo file ha scoperto che A
e B
erano delle varibili static
final
intere e che A
era inizializzata al valore 1 (mentre B
era una blank-final, ovvero una costante non ancora inizializzata a tempo
di dichiarazione, ma lo sarebbe stata più avanti a tempo di esecuzione).
Allora, come giustamente richiesto dalle specifiche
di Java, ha tradotto
"A="+Costanti.A+"
B="+Costanti.B
come fosse
stato
ha cioè
sostituito il riferimento a Costanti.A
con il valore 1 perché essendo A
di tipo final
il suo valore non sarebbe mai potuto cambiare dal suo valore iniziale 1.
Quando
poi abbiamo editato e ricompilato il solo file Costanti.java
è stato generato un altro file Costanti.class
dove A
effettivamente vale 3, ma il vecchio Main.class
non contiene più nessun riferimento alla variabile A,
bensì contiene direttamente il suo valore a tempo di compilazione,
cioè il valore 1. Ciò spiega perché ricompilando solamente
il file Costanti.java
si ottiene:
mentre
ricompilando invece entrambi i files si ottiene invece:
Se qualcuno
conosce e si ricorda il linguaggio C (o C++) può vedere la dichiarazione:
come fosse
una definizione di macro:
e quindi
tutti gli usi di A
in Main.java
sono sostituiti con il valore 1, a meno di non ricompilare successivamente
il file Main.java
quando A
vale 3.
Veniamo
quindi adesso alle domande che ci ponevamo nell'esercizio:
-
Qual'è
il modo corretto di inizializzare il valore delle nostre costanti?
-
In quali
casi si inizializza la costante nella stessa dichiarazione?
-
Quando
invece è bene inizializzare la costante in un blocco statico?
Il
modo corretto di inizializzare le costanti dipende da quanto effettivamente
un valore sia "costante". Alcune costanti sono certamente sempre
immutabili:
static final
double PI_HALPH = Math.PI/2;
static final
int ZERO = 0;
static final
int ONE = 1;
static final
String EMPTY_STRING = "";
altre
invece dipendono dai nostri gusti/linguaggi/intendimenti attuali che potremo
quasi sicuramente cambiare in un prossimo futuro:
static final
Color BACKGROUND_COLOR;
static final
boolean DEBUG;
static final
String ERROR_MSG;
static {
BACKGROUND_COLOR = Color.white;
DEBUG = true;
ERROR_MSG = "Failure in my program";
}
Così,
con riferimento all'esempio qui sopra:
-
forse
il mio gusto cambierà e non vorrò più il bianco come
colore di sfondo, ma sceglierò invece il nero;
-
molto
probabilmente fra qualche settimana avrò terminato lo sviluppo e
non vorrò più debuggare il mio codice;
-
forse
tra qualche mese dovrò presentare i miei errori in italiano piuttosto
che in inglese.
Quindi
nel primo caso ("vere" costanti) si usa la inizializzazione nella
stessa dichiarazione, mentre nel secondo caso ("false" costanti)
si usano i blocchi statici.
Il
nostro esercizio terminava con una domanda che qui ripropongo:
Se
per esempio una costante è utilizzata per controllare il livello
di trace della nostra applicazione, è corretto inizializzarla con
quale dei due modi?
// Costanti.java
class Costanti
{
static
final int debugLevelA = 3;
static
final int debugLevelB;
static
{
debugLevelB = 3;
}
}
Siamo
sicuri che i seguenti frammenti di codice facciano quello che ci aspettiamo?
// Main.java
// ...omissis...
if (Costanti.debugLevelA > 1)
System.out.println("Warning A");
// ...omissis...
if (Costanti.debugLevelB > 1)
System.out.println("Warning B");
// ...omissis...
Adesso
abbiamo imparato che la dichiarazione corretta ò quella di debugLevelB!
NOTA
BENE: Sotto questo punto di vista andrebbe anche rivista la rubrica
MokaHints relativa al Debugging
a livelli. Come andrebbe correttamente riscritta la classe astratta
Debug ivi presente? Lascio la soluzione del semplice esercizio al paziente
lettore che avrà avuto la sopportazione di seguirmi sino a questo
punto.
Bibliografia
L'argomento
di questo quiz è riccamente documentato dagli stessi estensori del
linguaggio Java. Si legga con attenzione il paragrafo
13.4.8 delle specifiche di Java. Alla fine del paragrafo in questione
si tratta anche del modo corretto con cui bisognerebbe inserire le istruzioni
di debugging e tracing all'interno di una applicazione Java (vedi e risolvi
il NOTA BENE qui sopra proposto).
Regole
del gioco
I quiz
presentati in questa rubrica trattano esclusivamente del linguaggio Java,
così non sono richieste conoscenze di applets, servlets, Beans,
etc.. Le uniche classi predefinite che possano eventualmente essere utilizzate
sono quindi solo quelle appartenenti al package java.lang (classi come
ad esempio: Thread, Class, ClassLoader, System, Runtime, etc...). Per risolvere
i quiz non serve inoltre editare, compilare o testare nessun tipo di codice.
Si tratta di quiz mentali che devono essere risolti solo con la
testa e non con il computer. Quindi:
-
Non bisogna
compilare ed eseguire nessun pezzo di codice: non sono interessato a sapere
che con certe implementazioni della JVM si ottengono certi risultati mentre
con altre implementazioni altri risultati. Sono invece interessato a quali
siano l'insieme di risultati validi così come specificato dal linguaggio
Java: cioè quali sono i risultati che un buon programmatore Java
si può aspettare da una qualsiasi JVM che soddisfi le specifiche
di Java?
-
Non occorre
che inviate una soluzione del quiz a me o alla rivista. La soluzione sarà
fornita nel prossimo numero. Una volta nota la soluzione (quindi solo dal
prossimo mese in poi) potete allora eventualmente contestarla e/o commentarla
inviandomi
una email. Io sarò ben lieto di pubblicare e discutere tutti
i pareri ricevuti.