a cura di Dario Dariol
 
    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
      "A="+1+" B="+Costanti.B
    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:
      A=1 B=4
    mentre ricompilando invece entrambi i files si ottiene invece:
      A=3 B=4
    Se qualcuno conosce e si ricorda il linguaggio C (o C++) può vedere la dichiarazione:
      static final int A = 1
    come fosse una definizione di macro:
      #define A 1
    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.
 




MokaByte®  è un marchio registrato da MokaByte s.r.l.

Java® è un marchio registrato da Sun Microsystems; tutti i diritti riservati

E' vietata la riproduzione anche parziale
Per comunicazioni inviare una mail a
mokainfo@mokabyte.it