Java: Невозможно использовать EnumSet в перечислении: Ошибка инициализации: Пример дерева талантов в области технических исследований

#java #casting #enums #initialization

#java #Кастинг #перечисления #инициализация

Вопрос:

Ошибка:

 ...
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.все, поскольку это не было «применимо к моей измененной переменной» … )

Я расширил этот пример, используя вторую реализацию: 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. Смотрите мой отредактированный ответ для вашей второй проблемы.

Ответ №1:

Ваша структура объявления настолько умна, что жаль, что она не работает. Но EnumSet очевидно, сначала необходимо полностью инициализировать перечисление. Он пытается извлечь массив констант из перечисления, чтобы, среди прочего, узнать, сколько места требуется для его внутреннего набора битов.

Вот одно из решений. Он использует вспомогательный метод, который сначала создает обычный набор ( HashSet ), а затем в блоке статической инициализации повторяет константы перечисления и заменяет все наборы на EnumSet s.

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

Комментарии:

1. ошибки инициализации больше нет, однако использование частного статического набора<BuildingTechTree> techsKnown; techsKnown = EnumSet.of(BuildingTechTree.NONE); приводит к тому, что techsKnown принимает значение null, поскольку оно по-прежнему копирует исходное значение «null» для NONE, похоже, не является глубокой копией… Попытается создать другой «конвертер», подобный вашему, только для этого

2. @user48573 Я не понимаю, что ты имеешь в виду. Это работает нормально. Это не обязательно должна быть «глубокая копия», потому что все константы enum являются одиночными.

3. Смотрите добавленную информацию в конце вопроса

4. @user48573 Эта проблема заключается в том, что techsKnown является статической переменной, которая инициализируется только в конструкторе экземпляра, который, предположительно, вы не вызываете.

5. Я сам только что пришел к такому же выводу, собирался исправиться, но вы меня опередили. В любом случае, помечено как ответ и 1, потому что ваш ответ не потребовал от меня редактировать целую кучу другого кода, объяснил причину, не используя абстрактные идеи, и объяснил, что делает ваш добавленный / измененный код. (о, и дополнение, хотя я скопировал объявление), а также продолжающаяся поддержка после ответа. Спасибо и высоко ценится, надеюсь, многие люди получат что-то от чтения этого вопроса / ответа!

Ответ №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));
    }
}
  

Комментарии:

1. Это также был правильный ответ, однако он потребовал от меня значительных изменений в моем коде. Кроме того, поскольку мой окончательный проект ожидал, что в этом перечислении будет более 100 зданий / единиц, этот ответ разделил бы каждое перечисление на две части, удвоив количество строк, тем самым снизив управляемость и читаемость. 1 за идею, я уверен, что найдется кто-нибудь, кто прочитает это и найдет ей применение. и за ответ и подробное объяснение моей второй проблемы