Wednesday, March 17, 2021

Compile-time constants

This seemingly simple concept has some quirks attached to it.

At the outset, it seemed like (or I understood it as) the variables that are static & final are compile time constants. 

I stumbled upon this when experimenting with inner classes. I will use the same example here to illustrate.

// Main.java
class OuterClass {

    class InnerClass {

        static final String CONSTANT = new String("A CONSTANT VALUE");

    }

}

So we have a static final String variable CONSTANT in the above code and is initialized by creating a new String object. We know that the value of CONSTANT cannot be changed after this initialization. 

But this does not make CONSTANT a compile time constant. Compiling the above code gives this verdict

>javac Main.java
Main.java:5: error: Illegal static declaration in inner class OuterClass.InnerClass
        static final String CONSTANT = new String("A CONSTANT VALUE");
                            ^
  modifier 'static' is only allowed in constant variable declarations
1 error

A little exploration and we find that the issue is in the way CONSTANT is initialized - using new String(). If we instead initialize it with String literal, it will be a compile time constant. Below code compiles without complaining

// Main1.java
class OuterClass {

    class InnerClass {

        static final String CONSTANT = "A CONSTANT VALUE";

    }

}

as does the below code, in which an expression concatenating two strings is assigned to CONSTANT 

// Main2.java
class OuterClass {

    class InnerClass {

        static final String CONSTANT = "A CONSTANT VALUE" + "THAT CANNOT CHANGE";

    }

}

But the below code which assigns null to CONSTANT complains its not compile time constant.

// Main3.java
class OuterClass {

    class InnerClass {

        static final String CONSTANT = null;

    }

}

Definition of a constant is given here 

A constant variable is a final variable of primitive type or type String that is initialized with a constant expression (§15.28). 

From this definition, a constant variable must be

  • Declared as final
  • Should be of primitive type or a String
  • Must be initialized when it is declared
  • And must be initialized with a constant expression

The definition of constant expression is given here

A key takeaway from this definition is a constant variable need not be static.

Stealing from the examples from the definition page, all the below are valid compile time constants

// Main4.java
class OuterClass {

    class InnerClass {

        final boolean CONSTANT1 = true;
        static final short CONSTANT2 = (short)(1*2*3*4*5*6);
        final int CONSTANT3 = Integer.MAX_VALUE / 2;
        static final double CONSTANT4 = 2.0 * Math.PI;
        final String CONSTANT5 = "The integer " + Long.MAX_VALUE + " is mighty big.";
        
    }
}