84

Is there any way to define static final variables (effectively constants) in a Java enum declaration?

What I want is to define in one place the string literal value for the BAR(1...n) values:

@RequiredArgsConstructor
public enum MyEnum {
    BAR1(BAR_VALUE),
    FOO("Foo"),
    BAR2(BAR_VALUE),
    ...,
    BARn(BAR_VALUE);

    private static final String BAR_VALUE = "Bar";

    @Getter
    private final String value;
}

I got the following error message for the code above: Cannot reference a field before it is defined.

jilt3d
  • 3,864
  • 8
  • 34
  • 41
  • 2
    why would you have so many enums with the same value? this seems totally against the idiom of enums! – Juvanis May 12 '14 at 12:21
  • 1
    Error text is answer for your problem !! – Not a bug May 12 '14 at 12:23
  • 3
    @Juvanis: notice that each enum instance has it own unique name so I don't consider it as a violation against the idioms of enums. In my case, instances of MyEnum acts as keys to a map which keys I want also to output to external target. These output values happens to be same in some cases according to the specification. – jilt3d May 12 '14 at 13:05
  • 2
    @KishanSarsechaGajjar: Nope, the answer is not there. I tried to put the constant declaration in first place, and I've got an another compilation error. – jilt3d May 12 '14 at 13:06
  • this works public enum MyEnum { BAR1(MyEnum.BAR_VALUE), FOO("Foo"), BAR2(MyEnum.BAR_VALUE), BARn(MyEnum.BAR_VALUE); private static final String BAR_VALUE = "Bar"; private final String value; MyEnum(final String value){ this.value =value; } } – Asterios Raptis Oct 01 '19 at 14:17
  • @jilt3d Interesting question, and the answers bellow are good, but why would you need to define these strings as constants? I don't see any benefit over using literals. Enums are unique instances so those strings are stored only once. There is no memory optimisation using static. – Roland Dec 23 '22 at 16:27

4 Answers4

106

As IntelliJ IDEA suggest when extracting constant - make static nested class. This approach works:

@RequiredArgsConstructor
public enum MyEnum {
    BAR1(Constants.BAR_VALUE),
    FOO("Foo"),
    BAR2(Constants.BAR_VALUE),
    ...,
    BARn(Constants.BAR_VALUE);



    @Getter
    private final String value;

    private static class Constants {
        public static final String BAR_VALUE = "BAR";
    }
}
Maciej Dobrowolski
  • 11,561
  • 5
  • 45
  • 67
36
public enum MyEnum {
    BAR1(MyEnum.BAR_VALUE);

    public static final String BAR_VALUE = "Bar";

works fine

shmosel
  • 49,289
  • 6
  • 73
  • 138
ThomasMorus
  • 387
  • 3
  • 2
  • 2
    I don't see any aspect that this answer adds to the existing and accepted answer. Could you please clarify your answer any further by providing a explanation? – Snickbrack Nov 29 '19 at 11:25
  • 16
    @Snickbrack I do. It eliminates the static nested class. Good answer. – user207421 Nov 30 '19 at 11:19
  • 1
    Could the upper case syntax cause confusion between enum values and constants? – borjab Feb 25 '20 at 15:31
  • 1
    @borjab yes if you use that method, you can't have a constant and an enum with the same name – Bentaye Mar 12 '20 at 11:45
  • 1
    Do you have an explanation why this works? I am wondering why explicitly using `MyEnum` makes a difference, because implicitly it is already there. – gillesB Jun 24 '20 at 05:55
  • 3
    Tried this and gives the same error: illegal forward reference in compilation time – raspacorp Nov 04 '20 at 02:17
  • Should fail during runtime! – bit_cracker007 Jul 21 '21 at 15:01
  • @bit_cracker007, I've just tried this and it works. Great for using in annotations. At first thought, seems better than the accepted answer – Guilherme Taffarel Bergamin Aug 06 '21 at 22:42
  • @gillesB, if you don't use `MyEnum`, it gives you an `Illegal forward reference` error. I think that you need to call the constant as if from another place because if not, in practice you would be using an attribute from an object that doesn't exist yet, so you need to statically access that value "from outside". – Guilherme Taffarel Bergamin Aug 06 '21 at 22:52
7
class Scratch {
    public static void main(String[] args) {
        System.out.println(MyEnum.BAR3); // 10:BAR3
    }
}
enum MyEnum {
//  BAR1(       foo),   // error: illegal forward reference
//  BAR2(MyEnum.foo2),  // error: illegal forward reference
    BAR3(MyEnum.foo);   // no error

    int x;

    public static final int foo =10;
    public static       int foo2=20;
    MyEnum(int i) {x = i;}

    @Override public String toString() { return x+":"+super.toString(); }
}

This can be done without an inner class for the constant. And field initialization works as expected (in case you don't know, it is possible to read static fields before they are initialized, you will read the default 0 or null, but it does not happen here).

18446744073709551615
  • 16,368
  • 4
  • 94
  • 127
ThomasMorus
  • 71
  • 1
  • 1
-7

Maybe you should considering breaking this enum into two fields: an enum and an int:

@RequiredArgsConstructor
public enum MyEnum {
    BAR("Bar"),
    FOO("Foo")

    @Getter
    private final String value;
}

And then use:

private MyEnum type;
private int value;

(You can put that into a class or not, whether it makes sense to you)

Luan Nico
  • 5,376
  • 2
  • 30
  • 60