4

I'm working on a Java project that uses a big class of constants like:

public final class Settings {
    public static final int PORT_1 = 8888;
    public static final int PORT_2 = 8889;
    ...
}

Now, some of the value of those constants are not available at compile time anymore so I need a way to "initialize" them at application starts (e.g. from the args[]). Once initialized there should be no way to change them. I'm not very skilled in java, how do I do this in an acceptable way?

I thought of using a singleton with something like a "one shot" set method that throws an exception if called more than one time but it seams too hacky...

flagg19
  • 1,782
  • 2
  • 22
  • 27
  • 2
    `final` variables can be assigned only once. You *can* assign them once from `args[]`. – Maroun Dec 30 '14 at 14:54
  • These constants are perfect and never going to be changed in lifetime of jvm.Why are you thinking of using Singleton? – SMA Dec 30 '14 at 14:55
  • @MarounMaroun I could assign them from args[] if I had them not static and in my main class but how do I do it if they are in the Setting class? I can't leave them unassigned there or it wont even compile – flagg19 Dec 30 '14 at 15:21

3 Answers3

3

You can use a static initializer like this:

public final class Settings {
  public static final int PORT_1;
  public static final int PORT_2;
  ...

  static {
    // create the value for PORT_1:
    PORT_1 = ...;
    // create the value for PORT_2:
    PORT_2 = ...;
  }
}

The static initializer is executed during class loading. The final keywords on PORT_1 and PORT_2 protects them to be changed afterwards.

vanje
  • 10,180
  • 2
  • 31
  • 47
  • This looks promising but how do I tell the static initializer to use the values the user has inserted as application parameters? – flagg19 Dec 30 '14 at 15:14
  • @flagg19 Read the user's settings (which I assume is a file, database record, set of Preferences nodes, etc.) inside the static initialization block. – VGR Dec 30 '14 at 15:39
  • I see... thanks, but assuming I get all those values as command-line parameters? Should I really write them on a file or Preference just to be able to read them in the static initializer? Isnt' it a quite common need or am I doing something strange? – flagg19 Dec 30 '14 at 16:09
  • It is a common situation, but normally user settings aren't specified on the command line. Typically, the user sets them via a user interface, your program saves them, and then during future starts, the program reads the saved information. – VGR Dec 30 '14 at 18:50
2

Well, using system properties is a way of doing it unless there is a huge amount of constants.

private static final String CONSTANT1 = System.getProperty("my.system.property");
private static final int CONSTANT2 = Integer.valueOf(System.getProperty("my.system.property"));

System properties are passed on the command line when starting the application using the -D flag.

If there are too many variables a static initializer can be used where a property file or similar can be read that holds the properties:

public class Constants {
    private static final String CONSTANT1 = System.getProperty("my.system.property");
    private static final int CONSTANT2 = Integer.valueOf(System.getProperty("my.system.property"));
    private static final String CONSTANT3;
    private static final String CONSTANT4;

    static {
        try {
            final Properties props = new Properties();
            props.load(
                new FileInputStream(
                        System.getProperty("app.properties.url", "app.properties")));

            CONSTANT3 = props.getProperty("my.constant.3");
            CONSTANT4 = props.getProperty("my.constant.3");
        } catch (IOException e) {
            throw new IllegalStateException("Unable to initialize constants", e);
        }
    }
}

Note that if you are using some external framework such as Spring Framework or similar there is usually a built-in mechanism for this. E.g. - Spring Framework can inject properties from a property file via the @Value annotation.

wassgren
  • 18,651
  • 6
  • 63
  • 77
  • This would work and would be the easiest solution, atm I've about 10 constants that would need to be set this way, but they might grow up to about 20. Is it considered a good practice or at least acceptable? The .jar file will be launched by a bash script so I don't really mind if it's a single line or it's huge. – flagg19 Dec 30 '14 at 16:20
  • It is definitely an acceptable approach. But, it the number of constants are *many* I believe that other approaches are better. One approach is to use a static initializer and read data from a file (to be able to externalize the constants) as I added to the answer. – wassgren Dec 30 '14 at 16:36
1

There is no simple way to do this in Java. One way to simulate this is to use a builder which returns an internal type (so it can write the private fields) but the internal type only has getters.

See this answer: https://stackoverflow.com/a/1953567/34088

Community
  • 1
  • 1
Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820