I am trying to initialize a static final variable. However, this variable is initialized in a method which can throw exception, therefor, I need to have inside a try catch block.
Even if I know that variable will be either initialized on try or on catch block, java compiler produces an error
The final field a may already have been assigned
This is my code:
public class TestClass {
private static final String a;
static {
try {
a = fn(); // ERROR
} catch (Exception e) {
a = null;
}
}
private static String fn() throws Exception {
throw new Exception("Forced exception to illustrate");
}
}
I tried another approach, declaring it as null directly, but it shows a similar error (In this case, it seems totally logic for me)
The final field TestClass.a cannot be assigned
public class TestClass {
private static final String a = null;
static {
try {
a = fn(); // ERROR
} catch (Exception e) {
}
}
private static String fn() throws Exception {
throw new Exception("Forced exception to illustrate");
}
}
Is there an elegant solution for this?
You can assign the value to a local variable first, and then assign it to the final variable after the try-catch block:
private static final String a;
static {
String value = null;
try {
value = fn();
} catch (Exception e) {
}
a = value;
}
This ensures a single assignment to the final variable.
Final variables can only be set once.
You cannot (and do not need to) set a to null in the catch block.
Make the following change:
public class TestClass {
private static final String a = setupField();
private static String setupField() {
String s = "";
try {
s = fn();
} catch (Exception e) {
// Log the exception, etc.
}
return s;
}
private static String fn() throws Exception {
return "Desired value here";
}
private static final String a = null;
properties that are final are only initialise once. Either in the Constructor or the way you did it here. You cannot give 'a' a new value after you have given it the value null. If you dont have a final you can set the value via the fn function
It's because final variable can only be assigned only once and it can't be reassigned again.
Nothing to do with try/catch
Related
This question already has answers here:
Change private static final field using Java reflection
(14 answers)
Closed 5 years ago.
When I try to change the value of a static final member of a nested static class I don't see it working. When I try the below code to change the static final member of some other class it works.
public class FinalPivateStaticMember {
public static void main(String[] args) {
System.out.println("Initial value == "+Test.val);
try {
Class cls = Class.forName("com.reflection.FinalPivateStaticMember$Test");
try {
Field file_systems_loaded = cls.getDeclaredField("val");
file_systems_loaded.setAccessible(true);
Field modifiers = Field.class.getDeclaredField("modifiers");
modifiers.setAccessible(true);
try {
System.out.println("--"+file_systems_loaded.getModifiers());
modifiers.setInt(file_systems_loaded,file_systems_loaded.getModifiers() & ~Modifier.FINAL);
System.out.println("--"+file_systems_loaded.getModifiers());
} catch (IllegalAccessException e) {
e.printStackTrace();
}
try {
file_systems_loaded.setBoolean(null,false);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
System.out.print("Final value == "+Test.val);
}
static class Test{
private static final boolean val = true;
}
}
Output of above code
Initial value == true
--26
--10
Final value == true
The value is actually changing, but you don't see the change in your print statements because of compiler optimization.
The compiler can (and will) replace the occurrences of primitive (and String) static final fields with the value they hold. So after compilation your print statements would be equivalent to
System.out.print("Final value == "+true)
Note that the variable is replaced with it's source value during compilation phase itself.
In this code, if I add 'final' to the variable definitions, I will receive "the final fields may have not been initialized" error. Some suggested solutions on Statckoverflow tend to be creating static functions to return the value. However, in this case I need to create four different functions to do that. Is there a more elegant solution to this issue?
private static String MODEL_PATH;
private static String VECTORS_PATH;
private static String NEG_PATH;
private static String POS_PATH;
static {
try {
MODEL_PATH = new ClassPathResource("models/word2vec_model").getFile().getAbsolutePath();
VECTORS_PATH = new ClassPathResource("models/model.zip").getFile().getAbsolutePath();
NEG_PATH = new ClassPathResource("models/neg.txt").getFile().getAbsolutePath();
POS_PATH = new ClassPathResource("models/pos.txt").getFile().getAbsolutePath();
} catch (Exception e) {
e.printStackTrace();
}
}
However, in this case I need to create four different functions to do that.
Since you are doing essentially the same thing, but with different resource names, one method would be sufficient:
private static String getResourceByName(string path) {
try {
return ClassPathResource(path).getFile().getAbsolutePath();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
Now you can use the same method four times in your initialization:
private static final String MODEL_PATH = getResourceByName("models/word2vec_model");
private static final String VECTORS_PATH = getResourceByName("models/model.zip");
private static final String NEG_PATH = getResourceByName("models/neg.txt");
private static final String POS_PATH = getResourceByName("models/pos.txt");
public class PropertyDemo {
private static InputStream in = PropertyDemo.class.getClassLoader().getResourceAsStream("address.properties");
public void test() {
try {
// InputStream in = PropertyDemo.class.getClassLoader().getResourceAsStream("address.properties");
Properties pro = new Properties();
pro.load(in);
String address = pro.getProperty("returnInfoRegister");
System.out.println(address);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
new PropertyDemo().test();
new PropertyDemo().test();
}}
In above code the first run will return correct value but the second run return null value I don't know why ,but when I change "in" variable to non static (I mean a local variable) things goes right but why?
When you move through the stream reading it, you are at the end of it. Using this as a static, saves that state (as it is the same variable in both class instances you have declared in main). So the next time you use it, it is already at the end of the stream. When you declare it as non-static, it is a new instance for each class instance and you are good.
But there really is no reason to declare this as a class level variable. Your inner variable is much better. You should also consider closing the stream at the end.
public class PropertyDemo {
//private static InputStream in = PropertyDemo.class.getClassLoader().getResourceAsStream("address.properties");
public void test() {
InputStream in = null;
try {
in = PropertyDemo.class.getClassLoader().getResourceAsStream("address.properties");
Properties pro = new Properties();
pro.load(in);
String address = pro.getProperty("returnInfoRegister");
System.out.println(address);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (in != null) {
try {in.close();} catch (Exception e) {}
}
}
}
public static void main(String[] args) {
new PropertyDemo().test();
new PropertyDemo().test();
}
}
I have two questions regarding the static block and Constants with below code.
Constant (or even simple Static variable) cannot be directly referrenced from static block. It gives error saying "Cannot reference a field before it is defined". But it is ok when accessing through a static method.
If I assign a value to a constant in static block's catch as mentioned below it gives error saying "The final field NAME may already have been assigned". But if asigning in catch it gives error saying "The blank final field NAME may not have been initialized".
I want to know why is it bahaving like this?
Code :
public class TestStaticblock {
static{
try {
// NAME = dummyStringValue() + NAME_APPENDER; // Cannot reference a field before it is defined
// NAME = dummyStringValue() + getNameAppender(); // This is OK
NAME = dummyStringValue();
} catch (Exception e) {
NAME = null; // The final field NAME may already have been assigned
}
}
private static String dummyStringValue() throws Exception{
return "dummy";
}
private static String getNameAppender() throws Exception{
return NAME_APPENDER;
}
private static final String NAME; // If I comment Catch it says "The blank final field NAME may not have been initialized"
private static String NAME_APPENDER = "appender";
}
You can only assign to NAME once (because it is final). Assign the result to a temporary variable, and then assign to NAME (and don't silently swallow Exceptions). Something like,
static {
String temp = null;
try {
temp = dummyStringValue();
} catch (Exception e) {
e.printStackTrace();
}
NAME = temp;
}
The reason you can't assign NAME the way your are currently is because the compiler performs static program analysis (specifically, the data-flow analysis) and that detects that there is a possible code path where NAME is not assigned. And because NAME is final, that is a compilation error.
You cannot use a static final field in a static block before it has been assigned, yet you can access it just by calling a method.
For example, this code prints null FOO:
public class Main {
static final String FOO;
static {
foo();
FOO = "FOOFOO".substring(0, 3);
foo();
}
static void foo() {
System.out.println(FOO);
}
public static void main(String[] args) {}
}
This is undeniably odd, but I guess it would have made the language considerably more complicated to make things such as this impossible.
As for your second question, this doesn't compile.
static{
try {
NAME = dummyStringValue();
} catch (Exception e) {
NAME = null; // The final field NAME may already have been assigned
}
}
This is also odd. If an exception is thrown it can only have occurred inside the method dummyStringValue(). Since you can't assign values to final fields inside a method, it is completely impossible for the NAME variable to have already been assigned in the catch block. Therefore there is no possible code path where NAME is not assigned. You'd think it ought to work in the same way as
static{
if (someCondition()) {
NAME = dummyStringValue();
} else {
NAME = null;
}
}
which compiles fine.
I guess the reason is again that it would have made the language much more complicated to allow this. There is no great benefit to allowing it as you can just use a method or a temp variable as indicated in the other answers. Exceptions just are more complicated than if statements - they can act almost like a goto. A good point was made by #ElliottFrisch in the comments. What about something like this:
static{
try {
NAME1 = dummyStringValue1();
NAME2 = dummyStringValue2();
} catch (Exception e) {
// Has NAME1 been assigned here?
}
}
Perhaps this would be of assistance to those looking for something similar.
There is a little-known feature of Java (discussed in JavaSpecialists Throwing Exceptions from Fields that if you wish to initialise a final instance variable (i.e. NOT a static) to the result of a method call that throws an exception then you can avoid the obvious error by adding a constructor that throws the exception.
Note that this solution only works for non-statics (not what you are observing).
public class TestStaticblock {
private final String NAME = dummyStringValue();
// Adding this removes the "unreported Exception" above.
public TestStaticblock() throws Exception {
}
private static String dummyStringValue() throws Exception {
return "dummy";
}
}
My strong personal preference is to use methods instead of static initializer blocks which initializer a single variable:
private static final String NAME = getName();
private static String getName() {
try {
return something();
} catch (Exception e) {
return null;
}
}
You don't get issues like the one you have described.
You can only calculate one field value, so you are not tempted to throw lots of things into the same block.
You can re-invoke a method to test it.
I have a class called Packet and a class called PacketClientConnecting witch extends it. The instances of PacketClientConnecting and other packets are stored in ArrayList<Packet>.
I want to have access to id value in static and non-static ways eg PacketClientConnecting.getStaticId() or packetArrayList.get(5).getId().
How can i do this without overriding two functions in every class?
I don't think there's a really smooth way of doing this, but one can achieve what you want by using reflection (only once: in the base class):
class Packet {
public static int getStaticId() {
return 1;
}
// This method is virtual and will be inherited without change
public int getId() {
try {
// Find and invoke the static method corresponding
// to the run-time instance
Method getStaticId = this.getClass().getMethod("getStaticId");
return (Integer) getStaticId.invoke(null);
// Catch three reflection-related exceptions at once, if you are on Java 7+,
// use multi-catch or just ReflectiveOperationException
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
}
Now in the subclass all you need is define getStaticId():
class PacketClientConnecting extends Packet {
public static int getStaticId() {
return 2;
}
}
Let's test it:
class Main {
public static void main(String[] args) {
// Both print 1
System.out.println(Packet.getStaticId());
System.out.println(new Packet().getId());
// Both print 2
System.out.println(PacketClientConnecting.getStaticId());
System.out.println(new PacketClientConnecting().getId());
}
}
If you want to avoid the overhead of calling reflective operations every time you call getId(), you can use a field in the base class to cache the id:
class Packet {
public static int getStaticId() {
return 1;
}
private final int id = computeId();
public int getId() {
return id;
}
// This method runs once per instance created
private int computeId() {
try {
Method getStaticId = this.getClass().getMethod("getStaticId");
return (Integer) getStaticId.invoke(null);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
}