Suppose that you have a Java project with several classes, most of them dealing with some constants (properties of your dataset, which are supposed to be loaded (based on your dataset) at the beginning of your experiment).
To become able to access these constants in the entire classes, a very reasonable option is putting the constants as fields of a container (object/class/Interface). Then for letting your other classes take advantage of the container field, you have 5 ways
Only if the field is dynamic:
Nevertheless of the field being dynamic or static (when the container is a class)
Only if the field is static
The first two options and the last one force you to write in your code tens or hundreds of time the container name + '.' (e.g. container.trainingListSize, container.numberOfUsers, Container.trainingListSize, Container.numberOfUsers, ...) which I am trying to get rid of that; the third option forces you to be the son of a special class which sometimes contradicts the structure of your program. It remains only the last option; but unfortunately in Java interfaces the constant values of interfaces are final and cannot be set, when you load your properties file, at the beginning of the program.
Looking at the question (Variables in Interface), it seems that the questioner was in a condition like me; but yet no solution I found.
I think you need to have clear intents and separation of concerns in your software architecture. You talk about "constants" but then you mention loading them from a file, so these are not really constants after all. These are more like configuration parameters. If that is correct, you have several options.
You can consider your configuration as a global variable: in that case I recommend creating a singleton class that loads the variables from the file and provides utility methods to access them, e.g., MyConfig.someParameter()
. Notice that you can static import MyConfig.*
and thus access the variables simply with someParameter()
.
You can consider your configuration as part of some other container, or context, in which case it's a lot harder to have a slim syntax as you want it, but you necessarily need to inject the context
and then call context.someParameter()
from your code. I wouldn't recommend making this context class a base class for other objects as it definitely violates separation of concerns and the OOP rule that if Foo
is not a Context
it shouldn't extend from it. Note that this is basically the first option you describe.
I should add that you can define members in interfaces only if they are static and final. This should not come as a surprise as java does not support multiple inheritance or "mixins". This was a very deliberate choice on the Java language specification team and I believe it was a good one. What you are trying to do does not really fit with the concept of an interface that you can magically extend and get all the members from, unless you used java 8 and defined default methods that rely on some map being read from a file or something, e.g.,
interface Config {
Map<String, String> getProperties();
default String someProperty() {
return getProperties().get("some-prop");
}
// ... more utility methods follow
}
The interface above defines a bunch (only one shown) of utility methods returning string properties (it can be any value really) keyed by strings. At startup, you could inject the map (read by a file for example) into your class(es) and have all those utility methods magically defined for you.
The code for a class using this approach would look like:
class MyExperiment implements Config {
private final Map<String, String> props;
MyExperiment(Map<String, String> props) { this.props = props; }
void someMethod() {
// read property from injected configuration
String someProp = someProperty();
}
}
This might save you some typing but will still require that your classes implement Config
and define the getProperties()
method, which IMO is a violation of separation of concerns and should be avoided.