Java:无法在枚举中使用枚举集:初始化错误:技术研究人才树示例

2022-09-04 02:11:26

错误:

...
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));

将EnumSet.of(NONE)和EnumSet.of(BARRACKS)替换为null,让初始化工作,但由于缺少数据结构,因此会破坏我的代码...显然,但我这样做是为了测试我的代码的其余部分,这并不是某种原因。

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

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

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

private static Set<BuildingTechTree> techsKnown;

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

添加到要从中调用以测试初始化的另一个类。不得不改变

public boolean researchTech(BuildingTechTree tech) {

到静态

这导致了相同的“不是枚举”错误。我没有任何代表,评论他的答案以指出初始化错误...

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

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;
}

答案 1

你的声明结构非常聪明,很遗憾它不起作用。但显然需要首先完全初始化枚举。它尝试从枚举中获取常量数组,以便除其他事项外,它还知道其内部位集需要多少空间。EnumSet

这里有一个解决方法。它使用一个帮助器方法,该方法首先创建一个普通集合 (),然后在静态初始化块中,它迭代枚举常量并将所有集合替换为 s。HashSetEnumSet

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);
            }
        }
    }
}

答案 2

你有一个先有鸡还是先有蛋的问题。您可以将枚举重构为如下所示:

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));
    }
}