【问题标题】:Force Initialization of an enumerated type in JavaJava中枚举类型的强制初始化
【发布时间】:2011-04-18 14:19:00
【问题描述】:

我正在尝试找到一种方法来强制 Java 加载/初始化枚举类型(嵌套在包含静态 Map 的类中)。

这对我很重要,因为枚举类型具有填充所述映射的构造函数,并且如果没有显式方式来初始化此枚举,映射将保持为空。我曾尝试使用Class.forName,但这似乎不起作用。

我想我可以创建一个枚举实例(并将其存储在其他集合或其他东西中),但我想知道是否有一种优雅的方式来做到这一点。

【问题讨论】:

  • 如果你不能保证枚举会在你需要使用地图之前被构建,你可能想在这里重新考虑你的设计,尽管马特关于使用静态初始化器的建议可能会让你得到你想要的想要。
  • 这个话题stackoverflow.com/questions/3798083/3800434#3800434 涉及到这个话题。
  • Class.forName 确实初始化了一个类。如果它“不起作用”,那么您的代码还有其他您没有意识到的问题。为什么不发布您的代码。
  • 基本上我看到一个异常说明该类是“无效的”,并且我确定我有正确的包路径。 Class.forName() 应该在所有情况下都可以处理枚举吗?

标签: java enums enumeration


【解决方案1】:

当你引用一个类时加载一个类。这对所有类都一样。

您遇到的问题很可能是在任何静态块之前初始化 Enum 值。即你不能在构造函数的静态块中引用初始化的东西。 (通常在构造函数中初始化静态内容是一个坏主意)您需要在静态块中初始化 Map,而不是在构造函数中。

试试

import java.util.Map; 
import java.util.HashMap; 

public enum EnumTest { 
  FOO, BAR, BAZ; 

  private static final Map<String,EnumTest> map = new LinkedHashMap<String,EnumTest>(); 
  static { 
      for(EnumTest e : EnumTest.values())
        map.put(e.name(), e); 
  } 

  public static void main(String... args) { 
    System.out.println(EnumTest.map); 
  } 
}

【讨论】:

  • 这是迄今为止最好的方法。我试试看。
  • 顺便说一句 +1 到垃圾神,因为他指出了一个使用相同方法的示例。
  • 成功了。我同意您的断言,即“在构造函数中初始化静态内容是一个坏主意”。我继续采用这种方法。
  • 我可能会在 Guava 或 Collections.unmodifiableMap 中使用 ImmutableMap 来防止在 EnumTest 中实际未定义的地图字段中添加任何潜在项目。
【解决方案2】:

你不能把地图的初始化放在 Enum 类型的静态初始化器中吗?

public enum SomeEnum
{
    Member1, Member2, Member3 ...

    private static Map<K, SomeEnum> map = ...;
    static 
    {
        ...populate map...
    }
    ...

编辑:似乎问题在于 Enum 成员定义需要放在首位。我想我只是掩盖了这一点。我修复了这个例子。

【讨论】:

  • 这似乎并不能解决问题,因为在 SomeEnum 中创建的所有实例都是静态的。问题仍然存在:我如何告诉类加载器初始化/加载枚举?一旦我解决了这个问题,我的 Enum 的所有静态代码都应该根据需要执行。
  • 那你能详细说明一下吗?这会导致什么问题?如果 Enum 进行初始化,如何在 Enum 初始化之前访问地图?地图不包含在枚举中吗?
  • 正确。该地图不包含在枚举中。而且我不能将地图放在枚举中,因为 Java 不允许枚举构造函数填充枚举中的静态集合。
  • +1 在 JLS 8.9 中有一个很好的例子,位于 enum Color" java.sun.com/docs/books/jls/third_edition/html/classes.html#8.9
  • 那么地图的目的是什么?我以前写过带有内部静态映射的枚举;我想我想知道你想要完成什么。
【解决方案3】:

你可以在枚举类中引用一些东西。例如:

public class EnumTest {
  static final Map<String, MyEnum> map = new HashMap<String, MyEnum>();

  enum MyEnum {
    FOO, BAR, BAZ;

    MyEnum() {
      map.put(name(), this);
    }
  }
  static {
    // ensure MyEnum is initialized
    MyEnum.values();
  }

  public static void main(String[] argsa) {
    System.out.println(map.size());
  }
}

【讨论】:

  • 这是我正在使用的基本解决方法。但是由于 MyEnum.values() 的返回值实际上并未在代码中使用,我担心它可能会被“优化”。再说一次,我看到了一个用于确保初始化常规类的类似技巧,所以也许这是解决问题的好方法。
  • 我相当肯定它不能完全优化,因为它可能会导致类初始化。与 C 或 C++ 相比,Java 留下的“未定义”要少得多,无论好坏。
【解决方案4】:

似乎这正是为什么经常建议使用访问器方法而不是直接引用成员的原因。您的问题是代码允许在初始化之前访问地图。阻止对地图的任意访问,并将其隐藏在确保已初始化的访问器方法后面。

import java.util.Map;
import java.util.HashMap;

public enum EnumTest {
  FOO, BAR, BAZ;

  private static Map<String,EnumTest> map = null;

  public synchronized static Map getMap() {
    if (map == null) {
      map = new HashMap<String,EnumTest>();
      for ( EnumTest e : EnumTest.values() ) {
        map.put( e.name(), e );
      }
    }

    return map;
  }

  public static void main(String[] args) {
    System.out.println( EnumTest.getMap().size() );
  }
}

【讨论】:

    猜你喜欢
    • 2021-01-23
    • 2014-10-21
    • 2014-08-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-16
    相关资源
    最近更新 更多