9

This seems like a crazy error, but Enum.valueOf(type, name) seems to be unstable on Oracle JDK 7 SE.

The manifestation is that, on the exact same name String (I've verified this), the call to valueOf() sometimes throws IllegalArgumentException with the message No enum constant ....

My team ran this through the Eclipse debugger, and what we noticed is that in the following JDK implementation of valueOf on enum, enumConstantDirectory(), i.e. the values() list for an enum, sometimes seem to be missing some values. Not the entirety of all the values defined in the enum itself.

I can work around the bug by calling Enum.valueOf(enumclass.class, "XXX") for all possible enum values on JVM startup. When I do this, it seems values() always contains the full set.

However, if I don't do this type of initialization, sometimes Enum.valueOf() will throw an IllegalArgumentException.

Context: I'm seeing this problem when using XStream 1.4.4 to convert POJO objects that convert enum, but the problem doesn't seem to be inherently with XStream.

Has anyone seen this kind of error? I would love to hear about it if you have. It boggles my mind. Is this a bug in the Oracle JDK/JVM implementation?

public static <T extends Enum<T>> T valueOf(Class<T> enumType,
                                            String name) {
    T result = enumType.enumConstantDirectory().get(name);
    if (result != null)
        return result;
    if (name == null)
        throw new NullPointerException("Name is null");
    throw new IllegalArgumentException(
        "No enum constant " + enumType.getCanonicalName() + "." + name);
}

Other relevant details:

We are using the org.reflections library to scan all enums in our code at startup. During the scanning, we take the type of the enum, and call clazz.getEnumConstants() on the Class object associated with the enum. This might be a relevant detail.

In looking and the java.lang.Class.getEnumConstants() implementation, it seems to share the same enumConstants shared object within the Class. I wonder if there's a problem with the implementation here.

Our enum is pretty simple, no static initialization, etc.

public enum ScreeningRuleType
{
  INSERT,
  CONFIRMATION,
  AMOUNT,
  EXISTENCE,
  BAN,
  SELECTION,
  CUSTOM; 

  private long id;
  private String descr;

  ScreeningRuleType()
  {
    id = this.ordinal();
    descr= this.toString().replace("_", " ");
  }
}

Edit: In experimenting with this, I'm finding another manifestation. After using the System.out initialization, now instead of throwing IllegalArgumentException, the value returned by Enum.valueOf seems to be random.

This shows what I'm seeing in the Eclipse debugger. It clearly shows I'm calling valueOf() on the string "EXISTENCE" and "EXISTENCE".intern(), and clearly shows it's returning AMOUNT() instead.

Debugger expression

4

2 回答 2

6

我忘记了我在哪里遇到过类似的东西——但我怀疑它是 Java 7 之前的版本,并且可能已经回到 Java 4/5 天。

问题在于,与 s 一起构建的伴随结构enum仅在第一次enum访问 s 时触发。不幸的是,如果您调用依赖这些结构的方法之一,valueOf或者EnumSet.allOf例如访问其中一个enums 之前,它通常会发生灾难性的失败。

可悲的是,我重构了代码,所以这不会发生,所以我不再手头有样本。我将尝试重现该问题并回复您。

我在这里看到 -为什么 Java 枚举常量初始化不完整?另一个发生的问题与演示。

于 2013-04-30T20:07:42.483 回答
4

其实我们发现了问题。这是一个脸+手掌的时刻。我们通过反射 API 破坏了枚举,方法是尝试对包含枚举的类进行深层(非浅层)副本。这具有清除单例枚举本身的所有实例变量的效果。感谢所有的帮助和建议。

于 2013-04-30T22:22:07.120 回答