I keep running into slight variations of a problem in Java and it's starting to get to me, and I can't really think of a proper way to get around it.
I have an object property that is final, but dynamic. That is, I want the value to be constant once assigned, but the value can be different each runtime. So I declare the class level variable at the beginning of the class - say private final FILE_NAME;. Then, in the constructor, I assign it a value - say FILE_NAME = buildFileName();
The problem begins when I have code in the buildFileName() method that throws an exception. So I try something like this in the constructor:
try{
FILE_NAME = buildFileName();
}
catch(Exception e){
...
System.exit(1);
}
Now I have an error - "The blank final field FILE_NAME may not have been initialized." This is where I start to get slightly annoyed at Java's strict compiler. I know that this won't be a problem because if it gets to the catch the program will exit... But the compiler doesn't know that and so doesn't allow this code. If I try to add a dummy assignment to the catch, I get - "The final field FILE_NAME may already have been assigned." I clearly can't assign a default value before the try-catch because I can only assign to it once.
Any ideas...?
How about
String tempName = null;
try{
tempName = buildFileName();
}
catch(Exception e){
...
System.exit(1);
}
FILE_NAME = tempName;
Either
try {
FILE_NAME = buildFileName();
} catch (Exception e){
...
System.exit(1);
throw new Error();
}
Or some prefer:
private static final String FILE_NAME = fileName();
private static String fileName() {
try {
return buildFileName();
} catch (Exception e){
...
System.exit(1);
throw new Error();
}
}
But calling System.exit in a static initialiser is probably a bad idea. It's going to mess your unit tests up.
On second thought, I think I just came up with a solution! - use an intermediate variable.
String fileName = null;
try{
fileName = buildFileName();
}
catch(Exception e){
...
System.exit(1);
}
FILE_NAME = fileName;
Don't know why it took me so long to think of this...
I would personally just throw an Error -- if your error flow is properly designed, the System.exit() should be redundant. Your program presumably doesn't plough on into the wilderness if an Error is thrown...?
Along the same lines as the OP's issue, I had to be able to find a way to assign values to final fields to be read in from a .properties file on the filesystem, so the values couldn't be known by my app until that happened. Using a generalized method call to assign the value after reading the content of the .properties file into a Properties object on app startup was a Hail Mary pass that thankfully worked out. It also limits the no. of times the file has to be read to once per the app's getting loaded into the memory simply by the code checking to see if the Properties object is or is not currently null. But of course, once assigned, the final field's value cannot be altered except by altering its "final" status via manuipulating the field's modifying definition at runtime (as discussed in some other places here on SO, such as https://stackoverflow.com/a/3301720/1216686 - sneaky, but I love it!). Code example, with typical runtime error checking such as for NPEs omitted for brevity:
import java.util.Properties;
public class MyConstants {
private static Properties props; // declared, not initialized,
// so it can still be set to
// an object reference.
public static String MY_STRING = getProperty("prop1name", "defaultval1");
public static int MY_INT = Integer.parseInt(getProperty("prop2name", "1"));
// more fields...
private static String getProperty(String name, String dflt) {
if ( props == null ) {
readProperties();
}
return props.getProperty(name, dflt);
}
private static void readProperties() {
props = new Properties(); // Use your fave way to read
// props from the file system; a permutation
// of Properties.load(...) worked for me.
}
// Testing...
public static void main(String[] args) {
System.out.println(MY_STRING);
System.out.println(MY_INT);
}
}
This lets you externalize properties to be read into the app and still mark the fields used to hold their values as "final". It also allows you to guarantee a returned value for the final field value since getProperty() in the Properties class allows the method's calling code to pass in a default value to use in case the property's key-value pair wasn't found in the external .properties file.
Related
I have a variable that asks me to make it final, but when making it final I get an error that says the following: "The final local variable "enter" cannot be assigned, since it is defined in an endclosing type"
How could you declare such a variable?
void yyyyyy(ActionEvent event){
final Scanner enter = null;
try{
.
.
.
enter = new Scaner(xxxxx);
}catch(){
....
}finally{
if(enter != null){
}
}
Right now you declare the variable and initialize it in the same time. Since it's final, you can not assign values to it more, than once.
If you remove assignment from initialization, you still can not make this variable final, because you check it for null in finally block. For that logic to work, variable should be assigned null explicitly somewhere. And that's impossible, because try block could throw an exception in the first line.
Whatever asks you to make the variable final - ignore it.
You can make it work like this. As some other answers are explaining, you can't assign value for final variable twice.
You can make it work like this
void yyyyyy(final ActionEvent event) {
Scanner enter = null;
try {
enter = new Scanner("/");
} catch (final Exception exception) {
} finally {
enter.close();
}
}
It might sound a bit strange, but is it possible to add the final modifier at runtime?
I have a variable which is marked as public static int/short. At certain point I want to prevent changing its value, and I want to keep its accessibility as a standard static value (ClassName.field).
public class Main {
private static int a = 0;
public static void addFinalMod(Field f) {
Field modifiersField = null;
try {
modifiersField = Field.class.getDeclaredField("modifiers");
}
catch (NoSuchFieldException e) {
e.printStackTrace();
}
modifiersField.setAccessible(true);
try {
modifiersField.setInt(f, f.getModifiers() & Modifier.FINAL);
}
catch (IllegalAccessException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
System.out.println(a);
try {
Field f = Main.class.getDeclaredField("a");
addFinalMod(f);
}
catch (NoSuchFieldException e) {
e.printStackTrace();
}
a = 10; //I was expecting error/exception here
System.out.println(a);
}
Output:
0
10
At certain point I want to prevent changing its value
Do that in application logic. Make the variable only accessible through methods. Keep a flag tracking any change to the variable. After you've applied the change or reached the certain point, raise the flag, and throw an exception for any further attempts at changing the variable.
final is a language keyword/feature for writing your source code and preventing re-assignment of a variable in source code. At runtime, it does (almost) nothing.
Use an immutable object:
public class Thing {
private int value;
public Thing(int value) { this.value = value; }
public int getValue() { return this.value; }
}
Then just replace it when it needs modification:
Thing x = Thing(1);
// do some stuff
x = Thing(2);
// more stuff
If you're thinking, "But then they can still modify the value by replacing the object!" well, of course. The only way to prevent that is to lock down some global state (which is actually impossible if it's not a constant), but you're not using global state, right? Global state is a Bad ThingTM.
Global state has a very simple problem: it makes your code much less reusable and more error prone. Error prone is especially true if you're modifying that global state at runtime. "Aw, crap, things are happening in the wrong order." You have no idea how easy that is. Trying to track order of events is the reason mutlithreading is so hard. In terms of reusability, it means that you can't have two independent instances of the thing, which in turn implies you can't really have any two instances of anything that depends on it (at least when it turns out you needed to vary this thing after all).
Even if this isn't a global, then this is some weird state that everyone who has to modify your code (including yourself) is going to have to be aware of and thinking about as they make changes. Does that sound like it's going to make changing things easier or harder? (Hint: the latter.) Don't do this to yourself or your coworkers. Make it simpler. Take a lesson from the Zen of Python: "Simple is better than complex."
So bottom line: if you need an immutable object, use an immutable object. If you need an immutable global that's not a constant, figure out a better way to organize your code.
static void goOut(String in) {
//instance variables
String fileCopy = currentLine + in;
try {
FileWriter writer = new FileWriter(output,true);
writer.write(line1 + System.getProperty("line.separator", "\r\n"));
writer.write(fileCopy + System.getProperty("line.separator", "\r\n"));
} catch(IOException ex) {
ex.printStackTrace();
}
}
Edited code to the correct standard as pointed out by other users.
of course because thats what you r telling it to do. every time is called it writes both x and the number. a quick fix: you can keep a flag if it is the first run set it flag = true. and check within ur method, sth like this:
public class YourClass{
private boolean didRun = false;
static void goOut(String in) {
...... init ur file and writer
if(!didRun)
writer.write(Y);
writer.write(in);
writer.close();
didRun = true;
}
}
I dont know the rest of the code but i think thats what u need
I believe you want to separate the jobs the "goOut" is responsible for.
You should make "goOut" only write the numbers (in your example).
The writing of the y's (in your example) should not be apart of the method and called once, at the start of writing to the file.
Also, #Jon Skeet is right about the multiple FileWriters. Use one, since its the same file.
Agree, sounds like a disaster.
When you use multiple writers to access the file, I would expect to get unpredictable results.
I dont think there is any guarantee that FileWriter1 would complete the task before FileWriter2.
In addition, the method is not synchronized.
At first I was going to make the question solely about the Image class, but I wanted to make it as broadly applicable as possible.
Basically, here's the scenario. I'm making a file for GUI constants, and in this file I'd like to have final variables for each of the Images I'm using. So my fields are declared like this UP_ARROW is:
public static final Image UP_ARROW;
Then I try to load them when the ImageIO API, like so:
static {
UP_ARROW = ImageIO.read(new File("img/upArrow.png"));
}
Unfortunately, this isn't valid, compilable code, because it explicitly throws IOException, which I have to deal with. So I modify it and surround it with a try/catch:
static {
try {
UP_ARROW = ImageIO.read(new File("img/upArrow.png"));
}
catch(IOException ioe) {
//TODO
}
}
Now I get a different compiler error. This time it says there's a possibility that the field may not have been initialized. Okay, that makes sense. Thank you for pointing that out to me, compiler. That seems like an easy fix:
static {
try {
UP_ARROW = ImageIO.read(new File("img/upArrow.png"));
}
catch(IOException ioe) {
UP_ARROW = null;
}
}
Now, no matter what, the UP_ARROW must be populated with either my image or null. I'm prepared to declare victory and move on. But now I get another, unexpected compiler error:
... Foiled again, compiler!
Hence the question: is there any way to get around this, such that I can dynamically load final fields at runtime? Or do I declare defeat and simply make the Images non-final?
Also, an explanation as to why the compiler won't allow this would be helpful as well. As I understand it, based on the code above, the UP_ARROW object could not have been assigned before reaching the catch{} block, because that's what must have thrown the exception. So if the try{} executes successfully, only one assignment takes place. If it does not execute successfully, still only one assignment take place. How is that not valid?
The following should do it:
static {
Image up_arrow = null;
try {
up_arrow = ImageIO.read(new File("img/upArrow.png"));
}
catch(IOException ioe) {
// log the error?
}
UP_ARROW = up_arrow;
}
It might make sense to enclose the final assignment in a finally block .
NPE's answer is good, but I think this one is (based off his and) better:
public enum Arrows {
UP ("img/upArrow.png"),
DOWN ("img/downArrow.png"),
LEFT ("img/leftArrow.png"),
RIGHT ("img/rightArrow.png");
public final Image myImage;
private Arrows(String fileName) {
Image tempImage;
try {
tempImage = ImageIO.read(new File(fileName));
} catch (IOException e) {
tempImage = null;
}
myImage = tempImage;
}
}
This solves your problem and gives you all the advantages of an enum over static final variables.
The Java tutorials recommend using the Preferences API over Properties files.
Properties files and ResourceBundles are the recommended way to handle Internalization requirements in applications.
I am considering using both for a desktop application that will display preferences in a locale specific way.
Can anyone point out problems with this approach?
Maybe I should just use Properties files period?
I am considering using both for a desktop application that will display preferences in a locale specific way.
OK, so what you want is translated configuration file in form of:
some_translated_key=some_value
Well, unless you want to support MUI at some point it should not be a big deal. However, if you do, so that different users on the same computer could use different languages, or user might be able to switch language, you would have troubles in matching key to a property. You would have to scan all translations while reading the key, and you would surely end up with multiple entries for the same key. How to resolve that? Well, that's a good question.
From my experience, configuration files should be language-independent (neutral culture) and should never be edited by hand (that is translating keys doesn't really matter).
I thought there could be a problem with character encoding, but following code snippet works without an issue (files are UTF-8 encoded):
public class Main {
private static final String FILE_NAME = "i18ned.properties";
private File propertiesFile;
private Properties properties;
public Main() {
properties = new Properties();
propertiesFile = new File(FILE_NAME);
if (propertiesFile.exists()) {
try {
properties.load(new BufferedReader(new FileReader(
propertiesFile)));
} catch (FileNotFoundException e) {
// not likely, but should be logged either way
} catch (IOException e) {
// logger should be used instead
e.printStackTrace();
}
}
}
public void saveProperties() {
try {
properties
.store(new BufferedWriter(new FileWriter(propertiesFile)), "");
} catch (IOException e) {
// oops, use logger instead
e.printStackTrace();
}
}
public static void main(String[] args) {
Main main = new Main();
main.storeSome();
main.readSome();
}
private void readSome() {
String highAsciiKey = "żółć";
String value = properties.getProperty(highAsciiKey);
System.out.println(value);
}
private void storeSome() {
String highAsciiKey = "żółć";
String highAsciiValue = "łąkę";
properties.setProperty(highAsciiKey, highAsciiValue);
saveProperties();
}
}
Using resource bundle for localizing applications is the standard way in java. The problems of this way are:
there is no compile time check of number and type of parameters required by resource.
It is hard to hold files clean, e.g. there is no mechanism the helps to remove unused strings
It is hard to make all texts translated to all supported languages.
etc....
The probably better internationalization mechanism is suggested by Google in their GWT. They generate class with method per string.
For example if you have text Hello, {0} they will generate method
String hello(String name);
So, you cannot pass neither 0 nor 2 arguments to this method. Only one.
This partially solves the second problem also. It is easier to see if method is not used in whole project. It does not solve the 3rd problem anyway.