Soluzione
Esercizio 005 - Ancora costanti
L'esercizio
riguardava ancora (è l'ultima volta, lo prometto!) l'uso del modificatore
final
in Java. Rivediamo e commentiamo l'esercizio.
La
seguente classe dichiara una costante s
inizializzata al valore ritornato dal metodo f
(che ritorna il valore della costante t)
e una costante t
inizializzata a 5. Quindi s
e t
valgono entrambi 5.
class JavaQuiz5
{
final
int s = f();
final
int t = 5;
int f()
{
return t;
}
Quindi
vengono dichiarate delle variabili ss
e tt
e un metodo ff
simili ai precedenti ma senza l'uso del modificatore final.
int ss
= ff();
int tt
= 5;
int ff()
{
return tt;
}
I valori
di ss
e tt
saranno eguali ai valori di s
e t
oppure il modificatore final
avrà influito in qualche modo? Verifichiamolo con il resto dell'esempio:
JavaQuiz5(){
System.out.println("s="+s +" ss="+ss+
" f="+f()+" ff="+ff());
}
public
static void main(String[]args) {
new JavaQuiz5();
}
}
che
compilato ed eseguito stampa:
prompt> javac JavaQuiz5.java
prompt> java JavaQuiz5
s=5 ss=0 f=5 ff=5
prompt>
Ecco
qui la sorpresa: s,
fe
ff
sono effettivamente tutti eguali a 5, mentre ss
vale zero. Ma s
e ss
non dovrebbero avere lo stesso valore? E quale è il valore corretto:
0 o 5?
La
risposta a queste due domande è complessa e dubbia. Vediamo di capire
il perché.
Se
ci riferiamo alla specifica
ufficiale di Java allora effettivamente s
e ss
dovrebbero avere lo stesso valore ed essere entrambi eguali a 0 perche'
la sezione
12.5 della specifica dice chiaramente al punto 4 che:
Execute
the instance initializers and instance variable initializers for this class,
assigning the values of instance variable initializers to the corresponding
instance variables, in the left-to-right order in which they appear
textually in the source code for the class.
La frase
evidenziata in neretto implica che s
e t
(e anche ss
e tt)
sono inizializzati nell'ordine dato (prima s
e poi t
e quindi anche prima ss
e poi tt)
e quindi quando viene invocata la funzione f(oppure
la ff)
per inizializzare s
( o ss),
t
(e anche tt)
vale ancora 0, perché t
(e anche tt)
sarà inizializzato a 5 solo dopo l'inizializzazione di s
(e anche ss).
Quindi secondo la specifica di Java la risposta corretta dell'esercizio
sarebbe:
Ma
allora perché il nostro interprete Java stampa invece la seguente
riga?
E'
un bug del nostro interprete? No. Tutti gli interpreti Java stampano la
stessa cosa. Il vero problema è che la specifica Java è scorretta!
Può sembrare strano ma è così. La specifica Java è
semplicemente un documento e come tale può avere degli errori (refusi)
o delle omissioni (non essere completo e non contemplare tutti i casi possibili).
In
questo esempio, in particolare, la specifica omette di dire che come con
i static fields durante l'inizializzazione delle classi, anche i final
instance fields che sono inizializzati a constant expressions sono inizializzati
prima degli altri instance fields. Così il precedente punto 4 della
sezione 12.5 della specifica dovrebbe recitare:
Execute
the instance initializers and instance variable initializers for this class,
assigning the values of instance variable initializers to the corresponding
instance variables, in the left-to-right order in which they appear
textually in the source code for the class, except that final instance
variables whose values are compile-time constants are initialized first.
Con questa
nuova e corretta specifica allora t,
che possiede un inizializzatore con la constant-expression 5, è
inizializzato prima di s
che possiede un inizializzatore con una espressione non costante f().
E quindi dapprima 5 è assegnato a t
e poi per s
si valuta la f()
che ritorna 5.
Questo
particolare errore nella specifica di Java è descritto in questa
pagina di Java Spec Report. Java
Spec Report è un sito che raccoglie, in maniera non ufficiale,
gli errata per le specifiche di Java. Pur non essendo un sito ufficiale
della Sun Microsystems la sua autorevolezza è grande e tutti i realizzatori
di compilatori e interpreti Java vi fanno costante riferimento.
Attualmente
Java Spec Report raccoglie quasi un centinaio (97 per la precisione) errata
riguardanti le specifiche di Java.