A better way to provide common data for enum s

Suppose, I want an int[] array to be shared by all instances of enum. Here is an example

public enum SampleEnum {
    Enum1(1), Enum2(2), Enum3(3), Enum4(4);

    private int[] values;

    private static final int[] SharedValues = {1, 2, 3, 4, 5};
    private static final int ValueCount = SharedValues.length;

    private SampleEnum(int factor) {
        // I prefer to calculate data once within constructor
        values = new int[ValueCount];
        for (int i=0; i<ValueCount; i++)
            values[i] = SharedValues[i] * factor;
    }

    private int[] getValues() {
       return values;
    }    
}

Guess what: I get message "Cannot refer to the static enum field within an initializer" for both ValueCount and SharedValues.

The problem can be overcome by placing the static array in a separate class:

class SampleEnumData {
    static final int[] SharedValues = {1, 2, 3, 4, 5};
}


public enum SampleEnum {
    Enum1(1), Enum2(2), Enum3(3), Enum4(4);

    private int[] values;

    private SampleEnum(int factor) {
        // I prefer to calculate data once within constructor
        int[] sharedValues = SampleEnumData.SharedValues;
        int valueCount = sharedValues.length;
        values = new int[valueCount];
        for (int i=0; i<valueCount; i++)
            values[i] = sharedValues[i] * factor;
    }

    private int[] getValues() {
       return values;
    }    
}

But this looks more as an awkward patch, than a logical solution.

It there a reason for not allowing a reference to a static class within enum initializer?

Jon Skeet
people
quotationmark

It there a reason for not allowing a reference to a static class within enum initializer?

It's a static field that you're not allowed access to, and there's a very good reason: the field will still have its initial value, because the enum members will be initialized before any later static field initializers. This is often a problem if you're trying to populate a map keyed by some aspect of the enum - you'd really like to perform the population in the constructor, but you can't because the map won't be created yet.

Options:

  • Perform all the values initialization in a static initializer block which is executed after all your instances have been constructed. The wrinkle here is that if you wish the field to be final, you'd need to create the array first, which means knowing how large to make it, without having access to the data. (Your choice of name is unfortunate here given the values() method which is also available here, mind you.)
  • Use a private nested class as a "holder" for the static field initialization. This is like your second solution, but without exposing the class.

The latter approach is probably the simplest, to be honest.

public enum SampleEnum {
    ...

    private static class SampleEnumData {
        static final int[] SHARED_VALUES = {1, 2, 3, 4, 5};
    }
}

people

See more on this question at Stackoverflow