问题 Java:无法在Enumeration:Initialization错误中使用EnumSet:Tech Research Talent Tree示例


错误:

...
Caused by: java.lang.ExceptionInInitializerError
...
Caused by: java.lang.ClassCastException: 
class com.evopulse.ds2150.TechTrees$BuildingTechTree
not an enum
at java.util.EnumSet.noneOf(Unknown Source)
at java.util.EnumSet.of(Unknown Source)
at com.evopulse.ds2150.TechTrees$BuildingTechTree.<clinit>(TechTrees.java:38)

这是我的枚举的片段

public enum BuildingTechTree {
//Name                      SoftName                    Requirements    
NONE                        ("NULL",                    null),

- >下一行是它崩溃的地方

BARRACKS                    ("Barracks",                EnumSet.of(NONE),
WALLS_SANDBAGS              ("Sandbag wall",            EnumSet.of(NONE),

POWERPLANT                  ("Power plant",             EnumSet.of(BARRACKS)),
GUARDTOWER                  ("Guard Tower",             EnumSet.of(BARRACKS));

用null替换EnumSet.of(NONE)和EnumSet.of(BARRACKS),让初始化工作,但由于缺少数据结构而破坏了我的代码......显然,但我做了它来测试我的其余代码不是不知何故原因。

删除EnumSet.of(NONE)并用NONE替换,对BARRACKS也一样,并且改变所有相关变量,构造函数和方法,这些都不起作用......(甚至无法使用contains.all ,因为是“不适用于我改变的变量”...)

我使用第二个实现扩展了这个例子: https://gamedev.stackexchange.com/a/25652/48573

我也尝试通过逐字复制示例来回溯我的步骤。添加

private static Set<BuildingTechTree> techsKnown;

techsKnown = (BuildingTechTree.BIODOME);
test = TechTrees.researchTech(techsKnown);

到另一个要调用初始化的类。并且不得不改变

public boolean researchTech(BuildingTechTree tech) {

静止的

这导致了相同的“in en enum”错误。我没有任何代表,评论他的答案,指出初始化错误......

添加了当前答案的信息,因为两种解决方案都会导致相同的新错误:

public class TechTrees {
private static Set<BuildingTechTree> techsKnown;

public TechTrees() {
    techsKnown = EnumSet.of(BuildingTechTree.NONE);       //Using this
    techsKnown = EnumSet.noneOf(BuildingTechTree.class);  //Or this
}

public static boolean researchTech(BuildingTechTree tech) {
    if (techsKnown.containsAll(tech.requirements)) {      //Causes null pointer
        return true;                                      //exception @ techsKnown  
    }
    return false;
}

12438
2017-07-05 07:38


起源

请参阅我编辑的答案,了解您的第二个问 - JB Nizet


答案:


你的声明结构是如此聪明,这是一种耻辱,它不起作用。但 EnumSet 显然需要首先完全初始化枚举。它试图从枚举中获取常量数组,以便除其他外,它知道其内部bitset需要多少空间。

这是一个解决方法。它使用辅助方法创建一个普通的集合(HashSet)首先,然后,在静态初始化块中,它迭代枚举常量并替换所有集合 EnumSet秒。

public enum BuildingTechTree {
    // Named constants
    //Name                      SoftName                        Requirements
    NONE                        ("NULL",                        null),
    BARRACKS                    ("Barracks",                    setOf(NONE)),
    WALLS_SANDBAGS              ("Sandbag wall",                setOf(NONE)),
    POWERPLANT                  ("Power plant",                 setOf(BARRACKS)),
    GUARDTOWER                  ("Guard Tower",                 setOf(BARRACKS));

    private final String softName;
    private Set<BuildingTechTree> requirements;

    private BuildingTechTree(String softName, Set<BuildingTechTree> requirements) {
        this.softName = softName;
        this.requirements = requirements;
    }

    private static Set<BuildingTechTree> setOf(BuildingTechTree... values) {
        return new HashSet<>(Arrays.asList(values));
    }

    static {
        for (BuildingTechTree v : values()) {
            if (v.requirements == null) {
                v.requirements = EnumSet.noneOf(BuildingTechTree.class);
            } else {
                v.requirements = EnumSet.copyOf(v.requirements);
            }
        }
    }
}

9
2017-07-05 08:02



没有更多的初始化错误,但使用私有静态Set <BuildingTechTree> techsKnown; techsKnown = EnumSet.of(BuildingTechTree.NONE);导致techsKnown为null,因为它仍然复制NONE的原始“null”值,似乎不是一个深拷贝...将尝试为你创建另一个“转换器” - user48573
@ user48573我不知道你的意思。它工作正常。它不需要是“深层复制”,因为所有枚举常量都是单例。 - Boann
请参阅问题底部的附加信息 - user48573
@ user48573那个问题是因为 techsKnown 是一个静态变量,只在实例构造函数中初始化,可能是你没有调用。 - Boann
我本人只是得出了同样的结论,我要纠正自己,但是你打败了我。无论如何,标记为答案和+1,因为你的答案,不要求我编辑一大堆其他代码,解释原因而不使用抽象的想法,并解释你添加/更改的代码的作用。 (哦,补充,即使我复制了声明),也是,回答后继续支持。谢谢,非常感谢,希望很多人会从阅读这个问题/答案中获得一些东西! - user48573


你有鸡和鸡蛋的问题。你可以将你的枚举重构成这样的东西:

public enum BuildingTechTree {

    NONE("NULL"),
    BARRACKS("Barracks"),
    WALLS_SANDBAGS("Sandbag wall"),
    POWERPLANT("Power plant"),
    GUARDTOWER("Guard Tower");

    static {
        NONE.trees = EnumSet.noneOf(BuildingTechTree.class);
        BARRACKS.trees = EnumSet.of(NONE);
        WALLS_SANDBAGS.trees = EnumSet.of(NONE);
        POWERPLANT.trees = EnumSet.of(BARRACKS);
        GUARDTOWER.trees = EnumSet.of(BARRACKS);
    }

    private String name;
    private Set<BuildingTechTree> trees;

    private BuildingTechTree(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public Set<BuildingTechTree> getTrees() {
        return Collections.unmodifiableSet(trees);
    }
}

编辑:

关于你的第二个问题:你是从静态方法访问一个静态变量。但是当调用类的构造函数时,这个变量被初始化(这是一个巨大的设计问题)。不要使用非最终的静态字段。并且不要从实例方法或构造函数初始化静态字段。这没有意义。您没有设置构建汽车时所有汽车应具有的颜色。静态初始化静态字段:

public class TechTrees {
    private static final Set<BuildingTechTree> TECHS_KNOWN =
        EnumSet.of(BuildingTechTree.NONE);

    public static boolean researchTech(BuildingTechTree tech) {
        return TECHS_KNOWN.containsAll(tech.requirements));
    }
}

6
2017-07-05 07:57



这也是一个有效的答案,但它要求我广泛地改变我的代码。此外,由于我的最终项目预计在此枚举中有超过100个建筑物/单位,因此这个答案会将每个枚举分成两部分,使行数增加一倍,从而降低可管理性和可读性。 +1的想法,我敢肯定会有人读这个并找到它的用途。以及对我的第二个问题的回应和详细解释 - user48573