If a field that was not final
is changed to be final
, then it can break compatibility with pre-existing binaries that attempt to assign new values to the field. For
example, if the program:
class Super { static char s; }
class Test extends Super { public static void main(String[] args) { s = 'a'; System.out.println(s); } }
is compiled and executed, it produces the output:
a
Suppose that a new version of class Super
is produced:
class Super { static char s; }
If Super
is recompiled but not Test
, then running the new binary with the existing binary of Test
results in a IncompatibleClassChangeError
. (In certain
early implementations of Java this example would run without error, because of a
flaw in the implementation.)
We call a field that is static
, final
, and initialized with a compile-time constant expression a primitive constant. Note that all fields in interfaces are implicitly static
and final
, and they are often, but not always, constants.
If a field is not a primitive constant, then deleting the keyword final
or changing the value to which the field is initialized does not break compatibility with existing binaries.
If a field is a primitive constant, then deleting the keyword final
or changing its value will not break compatibility with pre-existing binaries by causing them not to run, but they will not see any new value for the constant unless they are recompiled. If the example:
class Flags { final static boolean debug = true; }
class Test { public static void main(String[] args) { if (Flags.debug) System.out.println("debug is true"); } }
is compiled and executed, it produces the output:
debug is true
Suppose that a new version of class Flags
is produced:
class Flags { final static boolean debug = false; }
If Flags
is recompiled but not Test
, then running the new binary with the existing binary of Test
produces the output:
debug is true
because the value of debug
was a compile-time primitive constant, and could have
been used in compiling Test
without making a reference to the class Flags
.
This behavior would not change if Flags
were changed to be an interface, as in the modified example:
interface Flags { boolean debug = true; } class Test { public static void main(String[] args) { if (Flags.debug) System.out.println("debug is true"); } }
(One reason for requiring inlining of primitive constants is that Java switch
statements require constants on each case
, and no two such constant values may be
the same. Java checks for duplicate constant values in a switch
statement at compile time; the class
file format does not do symbolic linkage of case
values.)
The best way to avoid problems with "inconstant constants" in widely-distributed code is to declare as primitive constants only values which truly are unlikely ever to change. Many primitive constants in interfaces are small integer values replacing enumerated types, which Java does not support; these small values can be chosen arbitrarily, and should not need to be changed. Other than for true mathematical constants, we recommend that Java code make very sparing use of class variables that are declared static
and final
. If the read-only nature of final
is required, a better choice is to declare a private
static
variable and a suitable accessor method to get its value. Thus we recommend:
private static int N; public static int getN() { return N; }
public static final int N = ...;
public static int N = ...;
if N
need not be read-only. We also recommend, as a general rule, that only truly
constant values be declared in interfaces. We note, but do not recommend, that if a
field of primitive type of an interface may change, its value may be expressed idiomatically as in:
interface Flags { boolean debug = new Boolean(true).booleanValue(); }
insuring that this value is not a constant. Similar idioms exist for the other primitive types.
One other thing to note is that static
final
fields that have constant values (whether of primitive or String
type) must never appear to have the default initial value for their type (§4.5.4). This means that all such fields appear to be initialized first during class initialization (§8.3.2.1, §9.3.1, §12.4.2).