Question

I have a class holding a large a mount of generated constants as such:

public class Constants extends SomeBaseClass {

  // init() is defined in some base class...
  public static final XXX KEY1 = init(...);
  public static final XXX KEY2 = init(...);
  public static final XXX KEY3 = init(...);

  // ...
  public static final XXX KEY2000 = init(...);
}

When the number of generated constants is very high, this results in a static initialiser that is larger than the upper limit for Java method sizes (i.e. > 64kb), resulting in a compiler error. One solution is to create several "block initialisation methods" for blocks that can be guaranteed to produce less than 64kb of byte-code, such that they fit into a method:

public class Constants extends SomeBaseClass {

  public static XXX KEY1;
  public static XXX KEY2;
  public static XXX KEY3;

  // ...
  public static XXX KEY2000;

  static {
    initialise0001To1000();
    initialise1001To2000();
  }

  private static void initialise0001To1000() {
    KEY1 = init(...);
    KEY2 = init(...);
    KEY3 = init(...);
    // ...
  }

  private static void initialise1001To2000() {
    // ...
    KEY2000 = init(...);
  }
}

The drawback of this is that I can no longer declare the constants as final, because they are now no longer initialised directly in the static initialiser.

My question is, how can I circumvent that compiler / JVM limitation in a way that I can still generate static final constants?

Was it helpful?

Solution 2

I finally went for a solution involving nested classes. This was suggested in a comment to this answer here by user Loadmaster. Nested classes have two advantages:

  • They allow for hiding these workaround implementation details from the outside world by being private nested classes
  • They allow for keeping constants final

But they also have a disadvantage compared to templatetypedef's solution:

  • I will run into the same problem again with much larger numbers of constants

Right now, however, this seems to be the most suitable solution:

public class Constants {

  public static XXX KEY1    = Constants1.KEY1;
  public static XXX KEY2    = Constants1.KEY2;
  public static XXX KEY3    = Constants1.KEY3;

  // ...
  public static XXX KEY2000 = Constants2.KEY2000;

  // Nested class holding 1000 constants
  private static class Constants1 extends SomeBaseClass {
    KEY1 = init(...);
    KEY2 = init(...);
    KEY3 = init(...);
    // ...
  }

  // Nested class holding the next 1000 constants
  private static class Constants2 extends SomeBaseClass {
    // ...
    KEY2000 = init(...);
  }

  // Keep generating nested classes for more constants...
  private static class Constants3 ... {}
}

OTHER TIPS

One option would be to use inheritance - have a series of classes Constants1, Constants2, ..., ConstantsN that all define the constants, then have each one inherit from the previous one. The final class Constants can then directly inherit from the last of them. This also lets you mark everything final.

Out of curiosity, how did you end up with a file so large that you couldn't fit the initialization code into the 64KB limit?

Hope this helps!

Wouldn't this work? NO. See comments for why this kind of answer will not solve the problem.

It would allow you to keep the static variables final and it would be easier to autogenerate. However java collapses all the static init blocks into one giant static init block and thus the problem is not solved.

public class Constants extends SomeBaseClass {

  // init() is defined in some base class...
  public static final XXX KEY1 ;
  static
  {
       KEY1 = init(...);
  }
  public static final XXX KEY2 ;
  static
  {
       KEY2 = init(...);
  }
  public static final XXX KEY3 ;
  static
  {
       KEY3 = init(...);
  }

  // ...

}

You can have as many static initialization blocks as you want.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top