Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EnumSet doesn't think my enum is an enum [duplicate]

Error:

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

Here is a snippet of my enumeration

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

--> This next line is where it crashes

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

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

Replacing EnumSet.of(NONE) and EnumSet.of(BARRACKS) with null, lets initialization work, but breaks my code, due to missing data structure... obviously, but I did it to test the rest of my code wasn't somehow the cause.

Removing EnumSet.of(NONE) and replacing with just NONE, and the same for BARRACKS, and changing all related variables, constructor, and methods, that didn't work either... (and even couldn't use the contains.all, since is wasn't "applicable to my changed variable"... )

I extended this example, using the second implementation: https://gamedev.stackexchange.com/a/25652/48573

I also tried retracing my steps by copying the example verbatim. added

private static Set<BuildingTechTree> techsKnown;

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

to another class to be called from for testing initialization. and had to change

public boolean researchTech(BuildingTechTree tech) {

to static

This resulted in the same "in not an enum" error. I don't have any rep, to comment on his answer to point out the initialization error...

Added info for both current answers, as both solutions cause the same new error:

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;
}
like image 793
EvoPulse Avatar asked Nov 21 '22 23:11

EvoPulse


2 Answers

Your declaration structure is so clever it's a shame it doesn't work. But EnumSet apparently needs the enum to be fully initialized first. It tries to fetch the array of constants from the enum so that, among other things, it knows how much space is needed for its internal bitset.

Here's one workaround. It uses a helper method that creates an ordinary set (HashSet) first, and then, in a static initialization block, it iterates the enum constants and replaces all the sets with EnumSets.

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);
            }
        }
    }
}
like image 73
Boann Avatar answered Nov 23 '22 14:11

Boann


You have a chicken and egg problem. You could refactor your enum to something like this:

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

EDIT:

regarding your second problem: you're accessing a static variable, from a static method. But this variable is initialized when the constructor of the class has been called (which is a huge design problem). Don't use non-final static fields. And don't initialize static fields from instance methods or constructors. That doesn't make sense. You don't set the color that all cars should have when constructing a car. Initialize your static fields statically:

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));
    }
}
like image 38
JB Nizet Avatar answered Nov 23 '22 14:11

JB Nizet