Skip to content

java – Set final fields with reflection in Constructor

I’m trying to make a multi-language app with messages inside multiple *.properties files. I’ve started working on something like this:

    public Language(@NotNull Map<String, String> info) {
        Validate.notNull(info, "Language information cannot be null");

        this.PLUGIN_PREFIX = info.get("PLUGIN_PREFIX");
        this.ARGUMENT_CODE = info.get("ARGUMENT_CODE");
        // etc...
    }

Now, there’s a lot of messages, and I don’t feel like typing the same thing each time (plus there could me typos which could be an issue…).

The first solution I thought of was to loop through all of the fields that are like that (in caps, final, not static, etc.) and then use reflection to use the field name as a key to set it as the value. Obviously the compiler won’t let me because it thinks that the final field hasn’t been initialized.

Something like this:

    public Language(@NotNull Map<String, String> info) {
        Validate.notNull(info, "Language information cannot be null");

        Field[] fields = /* TODO get fields */ new Field[0];
        
        for (Field f : fields) f.set(f.getName(), info.get(f.getName()));
    }

Is there a way this can work? Or is there a better solution?

Edit: Quick naming conventions question, should these final “constants” be in upper case?

Answer

Usually, you don’t store text messages directly in constants, but rather just message keys. Then you use these keys to fetch the actual text messages in the map.

You can use a map directly, but in Java, there is ResourceBundle. A ResourceBundle can be loaded directly from a .properties file.

my-bundle_en.properties:

my.message=Hello, world!

my-bundle_fr.properties:

    my.message=Bonjour tout le monde!
    
    my-bundle_de.properties:
    
    my.message=Hallo Welt!
    
    

Something.java:

public static final MY_MESSAGE = "my.message";
    
ResourceBundle bundle = ResourceBundle.getBundle("my-bundle");
String text = bundle.getMessage(MY_MESSAGE);
    System.out.println(text);