Your hypothesis is almost right. It's not simply final
variables though, but so called "constant variables":
We call a variable, of primitive type or type String, that is final and initialized with a compile-time constant expression (§15.28) a constant variable. Whether a variable is a constant variable or not may have implications with respect to class initialization (§12.4.1), binary compatibility (§13.1, §13.4.9) and definite assignment (§16).
Then in the section on binary compatibility (§13.1):
References to fields that are constant variables (§4.12.4) are resolved at compile time to the constant value that is denoted. No reference to such a constant field should be present in the code in a binary file (except in the class or interface containing the constant field, which will have code to initialize it)
and (§13.4.9) (my emphasis):
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.
In a previous job, we made use of this to have a sort of conditional compilation system, so that we could produce production binaries with all the debug statements stripped out.
When you combine this with the way the javac
task determines what classes to recompile you get the behaviour you are seeing:
Only Java files that have no corresponding .class file or where the class file is older than the .java file will be compiled.
Note: Apache Ant uses only the names of the source and class files to find the classes that need a rebuild. It will not scan the source and therefore will have no knowledge about nested classes, classes that are named different from the source file, and so on. See the <depend>
task for dependency checking based on other than just existence/modification times.
The simplest way to fix this would be to do a clean full compile every time, with something like ant clean compile
(assuming you have a clean
target that removes all your class files). This might be too slow though.
I was also going to suggest that you look at the depend
task, as suggested in the docs for the javac
task, but looking at the docs for it (I haven't actually used it myself) it seems that it won't help (see the section titled "Limitations"):
The most obvious example of these limitations is that the task can't tell which classes to recompile when a constant primitive data type exported by other classes is changed.
One possible workaround, if you find doing a clean compile every time too slow, would be to make the values in your GUIConstants
class not be constants, at least whilst you make your changes. You could make the values non-final by commenting out all the final
keywords, then the other classes should see your changes. When you are happy with the new values, put the final
s back in and recompile (and test that everything is still working properly of course).