NilsHaldenwang
NilsHaldenwang

Reputation: 3667

Java Enum behaving very weird

I have defined the following Enum in my code:

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

}

When I am running a single JUnit test using the enum everything is fine. However, when I run the whole test suite the same test which worked in isolation before fails with null pointer exceptions, due to the enum not beeing correctly initialized, the output of the println in the enum constructor is:

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]

Most of the fields (Field also is an enum) are null for some reason I don't understand, but only if I execute the whole test suite.

I am totally clueless, any suggestions is much appreciated.

Thanks!

Upvotes: 3

Views: 134

Answers (2)

Andy Turner
Andy Turner

Reputation: 140484

I think you've got a class loading cycle between Table and Field.

Consider the following enums:

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

Because A references B, loading A causes B to be loaded; but B references A, which causes A to be loaded - except that it's already being loaded, so you see the uninitialized field values (null) when constructing Z.

Ideone demo

You need to identify this cycle (it may not be a cycle of length 2 like this, and may not only involve enum classes, so it might not be entirely obvious; tools exist to identify cyclical dependencies in code) and break it.

Upvotes: 5

Prateek Jain
Prateek Jain

Reputation: 3065

Tried to execute above given code and it worked perfectly for me, assuming Field is enum (attached here). It produced desired output with following code:

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 CONSTRUCTOR (base_prices): [BILLING_WEIGHT_LOWER_BOUND, BILLING_WEIGHT_UPPER_BOUND, BASE_PRICE, SERVICE_TYPE_ID, ZONE_ID]

ENUM CONSTRUCTOR (country_codes): [COUNTRY_CODE]

ENUM CONSTRUCTOR (service_types): [SERVICE_TYPE]

ENUM CONSTRUCTOR (zone_division): [COUNTRY_CODE_ID, SERVICE_TYPE_ID, ZONE_ID, POST_CODE_LOWER_BOUND, POST_CODE_UPPER_BOUND]

ENUM CONSTRUCTOR (zones): [ZONE_CODE]

Let me know if you are doing something different here.

Upvotes: 0

Related Questions