Java Enum ведет себя очень странно

#java #enums

#java #перечисления

Вопрос:

Я определил следующее перечисление в своем коде:

 public enum Table {
  BASE_PRICES("base_prices", Affinity.UPS,
          Arrays.asList(
                  Field.BILLING_WEIGHT_LOWER_BOUND,
                  Field.BILLING_WEIGHT_UPPER_BOUND,
                  Field.BASE_PRICE,
                  Field.SERVICE_TYPE_ID,
                  Field.ZONE_ID
          )),

  COUNTRY_CODES("country_codes", Affinity.UPS,
          Arrays.asList(Field.COUNTRY_CODE)),

  SERVICE_TYPES("service_types", Affinity.UPS,
          Arrays.asList(Field.SERVICE_TYPE)),

  ZONE_DIVISION("zone_division", Affinity.UPS,
          Arrays.asList(
                  Field.COUNTRY_CODE_ID,
                  Field.SERVICE_TYPE_ID,
                  Field.ZONE_ID,
                  Field.POST_CODE_LOWER_BOUND,
                  Field.POST_CODE_UPPER_BOUND)),

  ZONES("zones", Affinity.UPS,
          Arrays.asList(Field.ZONE_CODE));

  // used to denote the affinity of a table for
  // affinity specific DB cleanup and other stuff.
  public enum Affinity {
    UPS
  }

  private String name;
  private Affinity affinity;
  private List<Field> fields;

  private Table(String name, Affinity affinity, List<Field> fields){
    this.name = name;
    this.affinity = affinity;
    this.fields = fields;
    System.out.println("ENUM CONSTRUCTOR ("   name   "):");
    System.out.println(fields);
    System.out.println();
  }

  public List<Field> getFields(){
    return Collections.unmodifiableList(fields);
  }

}
 

Когда я запускаю один тест JUnit с использованием перечисления, все в порядке. Однако, когда я запускаю весь набор тестов, тот же тест, который работал изолированно до сбоя с исключениями нулевого указателя, из-за неправильной инициализации перечисления, вывод println в конструкторе enum:

 ENUM CONSTRUCTOR (base_prices):
[BILLING_WEIGHT_LOWER_BOUND, BILLING_WEIGHT_UPPER_BOUND, BASE_PRICE, null, null]

ENUM CONSTRUCTOR (country_codes):
[COUNTRY_CODE]

ENUM CONSTRUCTOR (service_types):
[null]

ENUM CONSTRUCTOR (zone_division):
[null, null, null, null, null]

ENUM CONSTRUCTOR (zones):
[null]
 

Большинство полей (поле также является перечислением) имеют значение null по какой-то причине, которую я не понимаю, но только в том случае, если я выполняю весь набор тестов.

Я совершенно невежественен, любые предложения очень ценятся.

Спасибо!

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

1. Это звучит как проблема с потоками (хотя и не уверен, почему). Вы пытались сделать поля (имя, аффинити и поля) окончательными, чтобы посмотреть, решит ли это вашу проблему?

2. Трудно сказать наверняка, не видя тестов, но мне кажется, что они разделяют какое-то состояние или ресурс, которые неправильно сбрасываются для следующего теста.

3. Что такое поле? Он содержит Table ?

4. Существует ли цикл загрузки класса между Table и Field ?

5. Не могли бы вы, пожалуйста, предоставить модульный тест и Field код?

Ответ №1:

Я думаю, у вас есть цикл загрузки класса между Table и Field .

Рассмотрим следующие перечисления:

 enum A {
    X(B.Z),

    A(B b) {
        System.out.println("Constructing "   name()   ": "   b);
    }
}

enum B {
    Z(A.X);

    B(A a) {
        System.out.println("Constructing "   name()   ": "   a);
    }
}
 

Потому A что ссылки B , загрузка A которых приводит B к загрузке; но B ссылки A , которые вызывают A загрузку — за исключением того, что они уже загружены, поэтому вы видите неинициализированные значения полей ( null ) при построении Z .

Ideone demo

Вам нужно определить этот цикл (это может быть не цикл длиной 2, подобный этому, и может включать не только классы enum, поэтому это может быть не совсем очевидно; существуют инструменты для определения циклических зависимостей в коде) и разорвать его.

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

1. Я добавил отчет об ошибке. Я думаю, компилятор должен хотя бы предупредить. Если компилятор отклонит его с ошибкой, было бы лучше. Это просто скрытая бомба NPE.

Ответ №2:

Попытался выполнить приведенный выше код, и он отлично сработал для меня, предполагая, что поле enum (прилагается здесь). Он выдал желаемый результат со следующим кодом:

 public class Runner {    
    public static void main(String[] args) {
        System.out.println(Table.BASE_PRICES);

    }    
}


public enum Field {
    BILLING_WEIGHT_LOWER_BOUND, BILLING_WEIGHT_UPPER_BOUND, BASE_PRICE, SERVICE_TYPE_ID, ZONE_ID, 
    COUNTRY_CODE, SERVICE_TYPE, COUNTRY_CODE_ID, POST_CODE_LOWER_BOUND, POST_CODE_UPPER_BOUND, ZONE_CODE;

}
 

КОНСТРУКТОР ENUM (base_prices):
[BILLING_WEIGHT_LOWER_BOUND, BILLING_WEIGHT_UPPER_BOUND, БАЗОВАЯ ЦЕНА, SERVICE_TYPE_ID, ИДЕНТИФИКАТОР ЗОНЫ]

КОНСТРУКТОР ENUM (country_codes): [COUNTRY_CODE]

КОНСТРУКТОР ПЕРЕЧИСЛЕНИЯ (service_types): [SERVICE_TYPE]

КОНСТРУКТОР ENUM (zone_division): [COUNTRY_CODE_ID, SERVICE_TYPE_ID, ZONE_ID, POST_CODE_LOWER_BOUND, POST_CODE_UPPER_BOUND]

КОНСТРУКТОР ПЕРЕЧИСЛЕНИЯ (зоны): [ZONE_CODE]

Дайте мне знать, если вы делаете здесь что-то другое.