Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Illegal forward reference in enum

Tags:

java

enums

I have an enum and I'm trying to pass a final static variable as parameter in constructor. Problem is that first statement in enums has to be a instance itself, but in that case, final variable is not defined yet. See the code and you will understand:

public enum ParseResource {

    PUSH(API_URL); //Error: illegal forward reference

    private final static String API_URL = "https://api.parse.com/1";

    private String url;
    private ParseResource(String url) {
        this.url = url;
    }
}

and the other option:

public enum ParseResource {

    //Illegal in enums, first statement has to be an instance
    private final static String API_URL = "https://api.parse.com/1";

    PUSH(API_URL);

    private ParseResource(String url) {
        this.url = url;
    }
}

How can I solve it? Thanks.

like image 330
Héctor Avatar asked May 11 '15 13:05

Héctor


1 Answers

There are two possible solutions which seem reasonable to me.

  1. Using a nested class (which is initialized separately):

    public enum ParseResource {
        PUSH(Constants.API_URL);
    
        private static class Constants {
            private static final String API_URL = "https://api.parse.com/1";
        }
    
        private String url;
        private ParseResource(String url) { this.url = url; }
    }
    

    That's the most generally useful, because it doesn't impose any important restrictions.

  2. Using a method:

    public enum ParseResource {
        PUSH(getApiUrl());
    
        private static String getApiUrl() { return "https://api.parse.com/1"; }
    
        private String url;
        private ParseResource(String url) { this.url = url; }
    }
    

    One hidden downside to using a method is that a method invocation isn't a constant expression, so it can't be used for some things, like annotation element values.


There's also a 3rd possible way which works in practice, but as of Java 9 is no longer guaranteed by the JLS to work, so it shouldn't be used.

This used the qualified name ParseResource.API_URL instead of the simple name API_URL to circumvent the forward reference error, because API_URL is a constant variable (i.e. initialized with a String literal in this case):

public enum ParseResource {
    PUSH(ParseResource.API_URL);

    private static final String API_URL = "https://api.parse.com/1";

    private String url;
    private ParseResource(String url) { this.url = url; }
}

In Java 8, the good behavior of this was specified by 8.3.2:

Note that static fields that are constant variables are initialized before other static fields. [...] Such fields will never be observed to have their default initial values.

However, due to this bug report, the wording was changed to the following in Java 9:

Note that static fields that are constant variables are initialized before other static fields. [...] When such fields are referenced by simple name, they will never be observed to have their default initial values.

The above code was not affected by the defect described in the bug report, but as of Java 9 it's no longer guaranteed to work.

like image 138
Radiodef Avatar answered Nov 15 '22 19:11

Radiodef