This question extends Initialize final variable before constructor in Java as I was not satisfied with the answer provided there.
I have the same question. I have variables that I need to be set as final but I cannot do so because I need to set them to values that require exceptions to be caught thus making it impossible unless I put them in the constructor. The problem with that is that I then have to make a new instance of the object every time I want to reference the final static variables which doesn't really make sense...
An example where path cannot be defined outside of the constructor nor inside the constructor unless a new instance is created each time the object is referenced from a different class:
public class Configuration {
private static final String path;
public Configuration() throws IOException, URISyntaxException {
propertiesUtility = new PropertiesUtility();
path = propertiesUtility.readProperty("path");
}
}
You can still use a static initialiser but you need some some embellishments for storing an exception which you ought to pick up at a later stage (such as in a constructor).
private static final String path;
private static final java.lang.Exception e_on_startup;
static {
java.lang.Exception local_e = null;
String local_path = null;
try {
// This is your old Configuration() method
propertiesUtility = new PropertiesUtility();
local_path = propertiesUtility.readProperty("path");
} catch (IOException | URISyntaxException e){
local_e = e;
}
path = local_path; /*You can only set this once as it's final*/
e_on_startup = local_e; /*you can only set this once as it's final*/
}
Related
I have set a private static properties object to load in some configuration for my factory class in the constructor of factory class.
public class BussinessServiceFactory {
private static final BussinessServiceFactory factory = new BussinessServiceFactory();
public static BussinessServiceFactory createBussinessServiceFactory(){
return factory;
}
private BussinessServiceFactory(){
InputStream in = BussinessServiceFactory.class.getClassLoader().getResourceAsStream("factory/bussinessservice.properties");
try {
bsConfig.load(in);
} catch (IOException exception) {
// TODO Auto-generated catch block
throw new RuntimeException(exception);
}
}
private static Properties bsConfig = new Properties();
public <T> T createBussinessService(Class<T> clazz){
try {
String clazzName = clazz.getSimpleName();
String name = bsConfig.getProperty(clazzName);
return (T) Class.forName(name).newInstance();
} catch (InstantiationException | IllegalAccessException
| ClassNotFoundException exception) {
throw new RuntimeException(exception);
}
}
}
However, when I initialize the class, it throws NullPointerException.
java.lang.NullPointerException
at factory.BussinessServiceFactory.<init>(BussinessServiceFactory.java:15)
at factory.BussinessServiceFactory.<clinit>(BussinessServiceFactory.java:8)
... 24 more
Then if I change the properties object into non-static object, the problem is solved. But I am still confused by the reason why would this happen and how it could be solved in this way.
static initializers are done topdown. So the static field factory is initialized when the class BussinessServiceFactory is loaded. This static initializer calls the constructor for BussinessServiceFactory, and so therefore, eventually, loads a reference to the static field bsConfig. This field is not yet initialized at this point, since we are still in the static initializer block for the factory field. Hence, the NullPointerException.
Fix this either 1) reordering the bsConfig field to be above the factory field, or better yet, don't have this interfield dependency by making the bsConfig field an instance field (which is what you did to fix it, and i believe is the best way to fix such an issue).
see http://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4 for details about order of static initialization.
addendum:
the stacktrace reveals the source of your problem. The clinit in the stacktrace stands for class init, and therefore, you can tell that the issue is loading of a class.
public class InstanceBuilder {
private static final InstanceBuilder INSTANCE = new InstanceBuilder();
private static String name = null;
private InstanceBuilder() {
System.out.println("Setting cons()");
name = "Testing";
}
public static String getName() {
return name;
}
}
public class Driver {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("value is " + InstanceBuilder.getName());
}
}
Output:
Setting cons()
value is null
Why does it print value as null even though I have set the static variable in constructor and it is called as expected. If I try to print in constructor, it is printing Testing, but if I call from public static method, it is null. I know if I change it to INSTANCE.name, it works.
But I want to understand why it is not working if I directly access static variable since same copy should be shared.
What I am missing here?
Because the value of name is getting modified after the constructor invocation as per the declaration order.
Lets see what is happening:
1) When you call InstanceBuilder.getName(), InstanceBuilder class is getting loaded. As part of that process INSTANCE and name instance variables are getting initialized.
2) While initializing INSTANCE,
private static final InstanceBuilder INSTANCE = new InstanceBuilder();
constructor of InstanceBuilder class is getting invoked & Setting cons() statement is getting printed and name variable is initialized with Testing.
3) Next name is again getting re-initialized with null due to the below statement
private static String name = null;
so when the method call returns to Driver class, value of name is null and null is getting printed. So even though name is declared as static, static has no role in that context.
Note:
Check the below link on the order of which class members should be declared
http://www.oracle.com/technetwork/java/javase/documentation/codeconventions-141855.html#1852
Here value is null because static initialization occurs as its declare in order,
So First your
`private static final InstanceBuilder INSTANCE = new InstanceBuilder();`
code execute and assign value to "testing", then your
`private static String name = null;`
code exceute and override value to null(as static variable have coppy per class only), so final value will be null.
So here this behavior is just beacuse of order of execution of static variable
How can I use the String variable transferred as the command parameter to a instance of another class in Java?
Like this:
public class NBody {
public static void main(double T, double dt, String filename) {
In filename = new In();
filename.readInt();
}
}
It notified as:
NBody.java:3: filename is already defined in
main(double,double,java.lang.String) In filename = new In();
What you are trying to do is define multiple variables which have the same name, so it is impossible. Imagine if it was possible, how would java runtime know which one you reference?
However if what you want is reference a variable which name's given as command line argument. You can use Reflection.
import java.lang.reflect.*;
public class Main {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
Field staticFieldMInt = Custom.class.getField("mInt"); // this can be argument
Integer mInt = (Integer) staticFieldMInt.get(null); // null because it is a class property.
System.out.println(mInt); // prints 10
}
}
public class Custom {
public static Integer mInt = 10; // this could be any type
}
You can't use runtime information (the content of a variable) to determine the name of a local variable. Variable names are a compile-time thing, not a runtime thing.
I have an activity with some final variables. I extracted their values (let's assume they're all Strings) into a resources file.
The problem:
If I directly assign them on the instantiation (as following):
private final String PREFERENCE_NAME = getResources().getString(R.string.preference_name);
I get the following error:
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.res.Resources android.content.Context.getResources()' on a null object reference
I understand the problem; the onCreate() method has not been called yet which means I cannot access context-related methods (getResources()).
If I want to assign the value in the onCreate() method of the activity, I get the error Cannot assign value to final variable 'PREFERENCE_NAME'
The question is: How can I get my final variables to be assigned from the resources file? And if this is not possible, what is the best practice for a solution?
Thanks in advance.
The simple answer is that you can't.
Variables declared final can only be set when the object is instantiated (i.e. in the constructor or with initialiser code).
Either use getResources().getString(R.string.preference_name); all the time or use a non-final variable.
The complex answer is that you can but that you shouldn't.
When you declare a variable final the compiler and VM uses this to make optimisations and assumptions. It can do this because the variable is guaranteed to never change. Changing it after it has been initialised can cause really weird bugs so you absolutely should not do that.
Here's how you do it:
public class FinalMessage {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
FinalMessage f = new FinalMessage("Hello World!");
System.out.println(f.getMessage());
f.changeFinalMessage("Hello Mars!");
System.out.println(f.getMessage());
}
private final String message;
public FinalMessage(String message) {
this.message = message;
}
void changeFinalMessage(String newMessage) throws IllegalAccessException, NoSuchFieldException {
final Field field = FinalMessage.class.getDeclaredField("message");
field.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(this, newMessage);
}
String getMessage() {
return message;
}
}
This will output:
Hello World!
Hello Mars!
Great, so we changed a final variable. No problem right?
Well, take this example instead:
public class FinalMessage {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
FinalMessage f = new FinalMessage();
System.out.println(f.getMessage());
f.changeFinalMessage("Hello Mars!");
System.out.println(f.getMessage());
}
private final String message = "Hello World!";
void changeFinalMessage(String newMessage) throws IllegalAccessException, NoSuchFieldException {
final Field field = FinalMessage.class.getDeclaredField("message");
field.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(this, newMessage);
}
String getMessage() {
return message;
}
}
This will output:
Hello World!
Hello World!
Wait, what?
The problem is that the compiler can see that the variable message is always going to be "Hello World!" so it inlines "Hello World!" instead of our call to f.getMessage(). If you run this in a debugger you will see the debugger reflect the updated message in the instance to "Hello Mars!" but since the variable is actually never accessed it won't affect the outcome of the program.
So to summarize: You can update final fields via reflection (granted that there is no Security Manager present that prevents you from doing it), but you should not do it since it can have very, very weird side-effects.
I am not responsible if your house gets termites or your cat catches on fire if you actually decide to implement this.
In this case I think the best thing to do it just make multiple calls to the resources. You still only have to change the value in one place and the call to getResources() isn't an expensive one.
Use your application context:
Create an application class:
public class MyApplication extends Application {
private static Context mContext;
#Override
public void onCreate() {
super.onCreate();
mContext = getApplicationContext();
}
public static String getStr(int resId) {
return mContext.getString(resId);
}
}
Use it in your manifest:
<application
android:name=".MyApplication"
...
Call it anywhere in your application:
static final String NAME = MyApplication.getStr(R.string.app_name);
You can use a factory pattern to solve this issue.
The builder is a class that aggregates the data needed to create your object, and when you are done - you just build your class.
In this approach, the data needed to generate the object is also available to the factory, and he can easily create the object, and initialize the final field when invoking its constructor.
You will have something like
class MyFactory {
private Resource getResources() { ... }
public MyObject build() {
String perference_name = getResources().getString(R.string.preference_name);
/...
return new MyObject(perfence_name ,....);
}
}
You can simply declare it as a final method:
private final String PREFERENCE_NAME() {
return getResources().getString(R.string.preference_name);}
I have following code:
public class LoadProperty
{
public static final String property_file_location = System.getProperty("app.vmargs.propertyfile");
public static final String application-startup_mode = System.getProperty("app.vmargs.startupmode");
}
It reads from 'VM arguments' and assigns to variables.
Since static final variable is only initialized at class load,
how can I catch exception in case some one forgets to pass parameter.
As of now, when I am using 'property_file_location' variable, exception is encountered in following cases:
If value is present, and location is wrong, FileNotFound exception comes.
If it is not initialized properly(value is null), it throws NullPointerException.
I need to handle second case at time of initialization only.
Similiar is case of second variable.
Whole idea is
To initialize application configuration parameters.
If successfully initialized, continue.
If not, alert user and terminate application.
You can catch it this way:
public class LoadProperty
{
public static final String property_file_location;
static {
String myTempValue = MY_DEFAULT_VALUE;
try {
myTempValue = System.getProperty("app.vmargs.propertyfile");
} catch(Exception e) {
myTempValue = MY_DEFAULT_VALUE;
}
property_file_location = myTempValue;
}
}
You can use a static initializer block as suggested by the rest of the answers. Even better move this functionality to a static utility class so you can still use them as an one-liner. You could then even provide default values e.g.
// PropertyUtils is a new class that you implement
// DEFAULT_FILE_LOCATION could e.g. out.log in current folder
public static final String property_file_location = PropertyUtils.getProperty("app.vmargs.propertyfile", DEFAULT_FILE_LOCATION);
However if those properties are not expected to exist all the time, I would suggest to not initialize them as static variables but read them during normal execution.
// in the place where you will first need the file location
String fileLocation = PropertyUtils.getProperty("app.vmargs.propertyfile");
if (fileLocation == null) {
// handle the error here
}
You may want to use a static bloc :
public static final property_file_location;
static {
try {
property_file_location = System.getProperty("app.vmargs.propertyfile");
} catch (xxx){//...}
}