Java: Unable to use EnumSet within an Enumeration : Initialization error : Tech Research Talent Tree example

前端 未结 2 814
野趣味
野趣味 2020-12-17 17:13

Error:

...
Caused by: java.lang.ExceptionInInitializerError
...
Caused by: java.lang.ClassCastException: 
class com.evopulse.ds2150.TechTrees$BuildingTechTre         


        
相关标签:
2条回答
  • 2020-12-17 17:31

    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);
                }
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-17 17:34

    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));
        }
    }
    
    0 讨论(0)
提交回复
热议问题