如何在初始化大量常量时绕过Java中静态初始化器的大小限制

如何在初始化大量常量时绕过Java中静态初始化器的大小限制,java,compiler-construction,compiler-errors,static-initialization,Java,Compiler Construction,Compiler Errors,Static Initialization,我有一个类包含大量生成的常量,例如: 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 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(...);
}
当生成的常量数量非常大时,会导致静态初始化器大于Java方法大小的上限(即大于64kb),从而导致编译器错误。一种解决方案是为可以保证产生小于64kb字节码的块创建几个“块初始化方法”,以便它们适合于一个方法:

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(...);
  }
}
这样做的缺点是,我不能再将常量声明为
final
,因为它们现在不再在静态初始化器中直接初始化


我的问题是,我如何才能绕过编译器/JVM的限制,以使我仍然能够生成
静态final
常量?

一个选项是使用继承-拥有一系列类
Constants1
Constants2
,…,
ConstantsN
,它们都定义了常量,然后让每一个继承上一个。最后一个类
常量
可以直接从最后一个继承。这还允许您标记所有内容
final

出于好奇,您是如何得到一个如此大的文件,以至于无法将初始化代码放入64KB的限制中的

希望这有帮助

这不管用吗?不可以。请参阅评论,了解这种回答无法解决问题的原因

这将允许您保持静态变量为最终变量,并且更容易自动生成。然而,java将所有静态init块压缩为一个巨大的静态init块,因此问题没有得到解决


您可以拥有任意数量的静态初始化块。

我终于找到了一个包含嵌套类的解决方案。这是在用户的评论中提出的。嵌套类有两个优点:

  • 它们允许通过
    private
    嵌套类对外部世界隐藏这些解决方案实现细节
  • 它们允许保留常量
    final
但与解决方案相比,它们也有一个缺点:

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 ... {}
}
  • 我将再次遇到同样的问题,常数的数目要大得多
然而,目前这似乎是最合适的解决方案:

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 ... {}
}

嗯,是的,这将是一个有效的解决办法,很好的想法。我还可以将所有常量放在接口中,并让
常量
实现所有这些常量……您也可以通过嵌套类来实现这一点,这样您就可以将所有内容都放在单个源文件中。@Loadmaster:那就更好了!这些嵌套类可以是私有的,顶级类可以引用嵌套类中的常量以公开它们,这可能会导致顶级类中的初始化器小得多-可能允许大约200k个常量。我认为这一评论值得一提answer@Loadmaster当前位置我已将你的想法记录在一份报告中。虽然这里的答案更一般(即支持更多常量),但我更喜欢您的想法,因为我可以向外界隐藏这些解决方案实现细节,您最终是如何遇到这个问题的?这段代码是从另一个文件自动生成的吗?@templatetypedef:这是的源代码生成器中的一个实际错误。它从数据库中生成主键、唯一键和外键作为常量对象。对于jOOQ来说,2000个键似乎太多了:您可以使用“虚拟”继承层来处理吗?有一个基类,它有一些非公共的使用名称,其中包含1000个常量,并且有一个静态初始值设定项来设置它们。然后一个派生类再增加1000个,一个子派生类再增加1000个,等等。?除了程序集中其他类的派生之外,只有派生最多的类才能用于任何目的。@supercat:我想你可以。该限制实际上是由类标题中的各种16位(两个字)标题字段施加的。所以,我猜这可以通过继承来避免。。。您必须验证字节码中是否已经有这样的答案(同时删除)。一个字节码类似乎只有一个静态初始化器。编译器将所有初始化语句合并到一个语句中,我认为这是一个遗憾。如果使用枚举,会发生同样的情况吗?缺点XXX必须是同一个类,您必须重新编写代码,以便init()是对枚举构造函数的调用。我不能使用枚举。真实世界的类型
XXX
具有泛型类型参数…:-/但我可以想象,在幕后,枚举与常规类共享相同的静态初始化器逻辑……是的。我做了一些复习。枚举也是如此。如果使用实例而不是静态变量,则此策略也不起作用。我将留下这个答案,而不是删除它(即使它是错误的)。b/c知道这种方法不起作用可能对其他人有用。我同意留下这个答案。除了这些评论,它也很有价值,这和答案一样。。。