J
Juha Laiho
I got recently bitten by something I consider to be a rather nasty
feature in the Java language, and decided to write up about it.
I had to do a couple of simple changes to a relatively shaky and
not-so-testable codebase, so I chose to do the absolute minimum
required: change initialization values for a couple of String fields
in a class, compile the changed class, and run the code manually to
observe that I had achieved the desired change.
What I didn't pay attention to initially was that the strings were
declared final. So, I changed the values, compiled the changed
class and replaced old classfile with new. Time for the test run,
and what I see is that the program still shows the original values.
A quick search through the source code shows that there is no other
component in the program containing a copy of these values except
the one I already changed - so, I'm starting to worry whether even
the version control for the code is out of synch with the actual
compiled code. This feeling did strenghten quite a lot when I found
out that another class (which directly uses the values of the fields
I changed) contains the old values of the fields. So, has there been
some other version of the _other_ class that at some point did
itself contain the fields that now need to be changed? I rummage
around, but do not find any such version of the source code for
the offending class. At a whim, I recompile the offending class
from source code fresh out of the version control, to observe any
possible differences in the compilation results. The results seem
pretty much similar, except that now the newly compiled class file
contains the changed value for the fields - which were declared
and initialized IN A JAVA SOURCE FILE FOR ANOTHER CLASS! (apologies
for raising my voice)
So, problem solved and programmer confused. Off I go to take a look
at the JLS, and find the explanation there in chapter 13.4.9:
: If a field is a constant variable (§4.12.4), 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 usage of the field unless they
: are recompiled. This is true even if the usage itself is not
: a compile-time constant expression (§15.28)
(with other things hinting to this same behaviour in a number of
places, among others in ch. 3.10.5)
In a word, ouch. A lesson learned.
feature in the Java language, and decided to write up about it.
I had to do a couple of simple changes to a relatively shaky and
not-so-testable codebase, so I chose to do the absolute minimum
required: change initialization values for a couple of String fields
in a class, compile the changed class, and run the code manually to
observe that I had achieved the desired change.
What I didn't pay attention to initially was that the strings were
declared final. So, I changed the values, compiled the changed
class and replaced old classfile with new. Time for the test run,
and what I see is that the program still shows the original values.
A quick search through the source code shows that there is no other
component in the program containing a copy of these values except
the one I already changed - so, I'm starting to worry whether even
the version control for the code is out of synch with the actual
compiled code. This feeling did strenghten quite a lot when I found
out that another class (which directly uses the values of the fields
I changed) contains the old values of the fields. So, has there been
some other version of the _other_ class that at some point did
itself contain the fields that now need to be changed? I rummage
around, but do not find any such version of the source code for
the offending class. At a whim, I recompile the offending class
from source code fresh out of the version control, to observe any
possible differences in the compilation results. The results seem
pretty much similar, except that now the newly compiled class file
contains the changed value for the fields - which were declared
and initialized IN A JAVA SOURCE FILE FOR ANOTHER CLASS! (apologies
for raising my voice)
So, problem solved and programmer confused. Off I go to take a look
at the JLS, and find the explanation there in chapter 13.4.9:
: If a field is a constant variable (§4.12.4), 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 usage of the field unless they
: are recompiled. This is true even if the usage itself is not
: a compile-time constant expression (§15.28)
(with other things hinting to this same behaviour in a number of
places, among others in ch. 3.10.5)
In a word, ouch. A lesson learned.