April 21, 2004

Inlining of final variables and recompilation

This problem that has bitten me in the ass a few times, and I'd love to hear any bright ideas on how y'all avoid it.

Suppose you have an interface that defines some useful constants:

public interface foo {
 int five = 6;
}

and a class that uses those constants:

public class bar {
 public static void main(String[]args) {
  System.out.println("five: "+foo.five);
 }
}

All well and good, until you realize that five isn't really 6, it's 5. Whoops, change the foo java file and rebuild, right? Well, if you use javac *.java to do this (as you might, if you only have the foo and bar files), then you'll be alright.

But, if you're like the other 99% of the java development world, and you use a build tool, like ant, smart enough to look at timestamps, you'll still get 6 for the output of java bar. Ant is smart enough to look at the timestamps of .class and .java files to determine which .java files have changed since it last did a compilation. But it is too dumb to realize that the bar class has a dependency on foo, and should thus be recompiled even though bar.java is older than bar.class. (I haven't looked at the byte code, but I expect that the value of five is just inlined into the bar class because it's a final variable.) If you're using a make based build system, I believe you can use javadeps to build out the correct dependency list, but I haven't seen anything similar for ant. Another options is to just remember to blow away your build directory anytime you change your 'constants'.

I guess this is why properties files might be a better choice for this type of configuration information, because they're always read in anew at startup, and thus cannot be inlined (since they're a runtime thing). Of course, then you lose the benefits of type checking. Not sure what the correct answer is.

Posted by moore at April 21, 2004 12:38 AM | TrackBack
Comments

Well, and what do you think about such code to prevent constant inlining?

public class foo {
   public static final int five;
   static {
      five = 5;
   }
}

or (especially for String constants)

public class foo {
   public static final String five = "five".toString();
}

Posted by: dmytro at July 21, 2004 09:35 AM | Permalink
© Moore Consulting, 2003-2006