2

I have been looking around for a good example on how I might go about dynamically generating enum values. I found a couple good articles, however I am looking for a compile time solution and what I've found is only at run time.

Does anyone know if this is even possible? I have yet to find anything that hints it might be.

Thanks!

EDIT: For clarification: I would like to be able to read values out of a database and populate an enum with those values.

In a perfect world, I would like my enum class to look like the following:

public static enum STATE {

    /* populated from DB if possible */
    MA("high taxes", 6),
    NH("low taxes", 3),
    ...
    ...

    private String desc;
    private in rating;

    public STATE (String description, int rating) {

        this.desc = description;
        this.rating = rating;
    }
}
15
  • 6
    "Dynamically Generate enum... however I am looking for a compile time solution" dynamic usually means "at runtime* so if you mean something else you need to explain it better. Commented Jul 27, 2015 at 20:52
  • I would like to be able to read values out of a database and populate an enum with the values. Commented Jul 27, 2015 at 20:54
  • How would you later want to use that Enum? Commented Jul 27, 2015 at 20:55
  • MyEnum currentState = MyEnum.STATES.MA where all 50 would be read in from the database at app initialization. Commented Jul 27, 2015 at 20:56
  • 2
    So theoretically you don't know at compile time that you even have a STATES.MA, because you only know what the 50 states are at runtime. Commented Jul 27, 2015 at 21:00

3 Answers 3

4

Well, here is an approach that does it on class initialization. Which is runtime:

public enum States {

    MA, NH; // ...

    private String description = "Description of " + name() + " not found in database.";
    private int rating;

    // Static initialization is performed after the enum constants
    // are initialized, but can still change *non-final* fields
    // in the constants
    static {
        String sql = "SELECT abbreviation, description, rating "
                    +"FROM states "
                    +"WHERE abbreviation IS NOT NULL ";

        ResultSet rs;

        // Open connection, create statement, execute, retrieve
        // result set. IMPORTANT: catch and properly handle all
        // checked exceptions, or else you'll get a nasty
        // initialization error. OTOH, you may not want your
        // application to start if this fails.

        while ( rs.next() ) {
            String abbreviation = rs.getString(1);
            String description  = rs.getString(2);
            int    rating       = rs.getInt(3);

            States st;

            try {
                // Get the enum constant that matches the abbreviation.
                st = valueOf(abbreviation);
                // Set the values in that constant
                st.description = description;
                st.rating = rating;
            } catch ( IllegalArgumentException e ) {
                // This exception happens when the abbreviation
                // doesn't match any constant. If you don't put
                // anything here, such values will be silently
                // ignored. If you don't catch, such values will
                // throw an initialization Error.
            }
        }

        // Clean up all database-related stuff.
    }

    // Only getters, no setters, as values are all
    // set from database in the static initialization.

    public String getDescription() {
        return description;
    }
    public int getRating() {
        return rating;
    }

}

With this definition, you can use the enum constants in your program, and the values in the description and rating field will be loaded at class initialization from database. Note that I gave a default value to description which will show up if the particular state's abbreviation is not in the database.


But as I said, this is run time. Although not completely impossible, I can see no sense in loading the values from database at compile time, as these values will stay fixed when you use your resulting .class file or jar. When you change values in your database, the values seen by the application will still be the one hard-compiled into the enum. In fact, you won't even need the database to be up to run the application.

But if you insist on doing this for some reason, well, no IDE will support this directly, I suppose. But you could probably write a script that manipulates the text of your enum java file, and use that script in a pre-compile phase in your build tool (maven, ant...). You'll probably need to write your class much like the above, only with the static initializing block empty. You'll need a clean copy outside of your src directory, and run the script so that it fills up the static initialization block with text derived from the database, and writes the result inside your src directory.

In short: not recommended, system/tool dependent, not useful, but also not impossible.

Sign up to request clarification or add additional context in comments.

Comments

3

You can use a class for this at the exact same place where you would have put the enum and get similar behaviour:

public final class STATE {
    public static final STATE MA;
    static {
        // SELECT desc, rating FROM myTable where name = 'MA' ... or what suits you
        ...
        MA = new STATE(myDesc, myRating);
    }
    ...
    private String desc;
    private int rating;
    private STATE (String description, int rating) {
        this.desc = description;
        this.rating = rating;
    }
    public String getDesc() {
        return desc;
    }
    ...
}

Because of the private constructor and because the class is final and only has getters you can only assign the predefined values to STATE. This means you can compare a STATE variable v just like that v == STATE.MA because they all use the same reference.

4 Comments

Enum does'nt make sense here
I'm unsure because I just don't like to use enums if they represent more than just an int, doesn't feel ituitive to me. Prefer a custom class or a Map then.
To be more precise, not even an int, in my opinion a value of an enum should just stand for the name of the value itself and the value behind it shouldn't even matter.
One example would be a general sort method which lets you specify the algorithm, so you could say SORT.QUICKSORT, SORT.MERGESORT and some others (SORT is a parameter of this method). As we can see the value behind QUICKSORT etc. doesn't matter, it is only needed to decide which algorithm to use. Here an enum would be a good choice.
1

If you have fixed names, but the "values" are loaded from the database, you can use an enum constructor:

public enum Data {
    A("a"), B("b"), C("c");

    SomeType someName;

    public Data(String s) {
        someName = MyDatabase.loadValue(s);
    }

    public SomeType getSomething() {
        return someName;
    }
}

The constructor is called at class initialization.

8 Comments

One possibly big limitation: this solution assumes that number of values read from DataBase is fixed.
Also how exactly we should call that constructor and pass to it proper values? Enum constructor is private.
@Pshemo It's the system that calles the constructors, at class init.
I was referring to "you can use an enum constructor" from your answer. Now after your comment it seems that by you you mean system. So how exactly can we set up system to call constructor with values from database?
@Pshemo By providing a custom constructor, that is - in turn - called by the system. Think of a static block. Note, the enum values has a pair of parenthesis. Please refer to the documentation.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.