Using an enum in Java to represent keys in a ResourceBundle

Viewed 2284

I have been tinkering with this idea for a few days, and I was wondering if anyone else has thought of doing this. I would like to try and create a ResourceBundle that I can access the values with by using an enum. The benefits of this approach would be that my keys would be well defined, and hopefully, my IDE can pick up on the types and auto-complete the variable names for me. In other words, I'm after a sort of refined ListResourceBundle.

Essentially, this is what I'm after...

I have an enum that consists of various bundles set up like so:

interface Bundle {
    String getBundleName();
    EnumResourceBundle<??????> getEnumResourceBundle();
}

enum Bundles implements Bundle {
    BUNDLE1("com.example.Bundle1", Keys.class);

    private final String bundleName;
    private final EnumResouceBundle<??????> bundle;

/**
 * I understand here I need to do some cast with ResourceBundle.getBundle(bundleName);
 * in order to have it back-track through parents properly. I'm fiddling with this
 * right now using either what I specified earlier (saving bundleName and then
 * retrieving the ResourceBundle as needed), and saving a reference to the 
 * ResourceBundle.
 */
    private <E extends Enum<E> & Key> Bundles(String bundleName, Class<E> clazz) {
        this.bundleName = bundleName;
        this.bundle = new EnumResourceBundle<??????>(clazz);
    }

    @Override
    public String getBundleName() {
        return bundleName;
    }

    @Override
    public EnumResourceBundle<??????> getEnumResourceBundle() {
        return bundle;
    }
}

interface Key {
    String getValue();
}

enum Keys implements Key {
    KEY1("This is a key"),
    KEY2("This is another key");

    private final String value;

    private Keys(String value) {
        this.value = value;
    }

    @Override
    public String getKey() {
        return value;
    }
}

class EnumResourceBundle<E extends Enum<E> & Key> extends ResourceBundle {
    // Can also store Object in case we need it
    private final EnumMap<E, Object> lookup;

    public EnumResourceBundle(Class<E> clazz) {
        lookup = new EnumMap<>(clazz);
    }

    public String getString(E key) {
        return (String)lookup.get(key);
    }
}

So my overall goal would be to have to code look something like this:

public static void main(String[] args) {
    Bundles.CLIENT.getEnumResourceBundle().getString(Keys.KEY1);
    Bundles.CLIENT.getEnumResourceBundle().getString(Keys.KEY2);

    // or Bundles.CLIENT.getString(Keys.KEY1);
}

I'd also like to provide support for formatting replacements (%s, %d, ...).

I realize that it isn't possible to back-track a type from a class, and that wouldn't help me because I've already instantiated Bundles#bundle, so I was wondering if I could somehow declare EnumResourceBundle, where the generic type is an enum which has implemented the Key interface. Any ideas, help, or thoughts would be appreciated. I would really like to see if I can get it working like this before I resort to named constants.

Update: I had a thought that maybe I could also try changing EnumResourceBundle#getString(E) to take a Key instead, but this would not guarantee that it's a valid Key specified in the enum, or any enum for that matter. Then again, I'm not sure how that method would work when using a parent enum Key within a child EnumResourceBundle, so maybe Key is a better option.

1 Answers
Related