21

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...?

6
  • You mean private static final FILE_NAME;? Commented May 5, 2010 at 14:10
  • 2
    @Tom Hawtin - no. Why should it be static? Commented May 5, 2010 at 14:15
  • I agree with Ryan's answer (and voted up appropriately). One thing I'll say though is... do you really want to System.exit if you can't initialize? Perhaps the best idea is to just let the exception get thrown - either way if it's not handled you'll still exit, but if there is a handling mechanism in place, it can be taken care of properly. Commented May 5, 2010 at 14:29
  • 1
    @froadie: because ALL_CAPS indicates constants (a.k.a. static final): java.sun.com/docs/codeconv/html/CodeConventions.doc8.html#367 Commented Jun 16, 2010 at 9:09
  • 1
    @froadie: exactly. And at least the sun naming convention says that "variables declared class constants" should be named with ALL_CAPS. Other fields, even if final, should be camelCase with a lower case starting character. Commented Jun 17, 2010 at 7:50

5 Answers 5

20

How about

String tempName = null;
try{
   tempName = buildFileName();
}
catch(Exception e){
   ...
   System.exit(1);
}
FILE_NAME = tempName;
Sign up to request clarification or add additional context in comments.

2 Comments

thanks :) I've been facing this problem for a while, and this only just hit me... Should've been an automatic reaction to the problem as it seems to have been with you
Heh, yeah. I think you beat me to it by just a few seconds ;)
7

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.

4 Comments

In the first example - why does throwing a new error make it pass the compiler? And why is it less ugly to add a line that will never be reached than it is to assign a temporary string a default value?
@froadie The details of the definite assignment rules are in the JLS. They are very tedious, but you should be able know what is going on by example. The compiler is required to detect that the variable is definitely assigned if an exception is not caught, and if an exception is caught the code leaves via an exception and is therefore not required to definitely assign the variable. (For final instance fields there are sneaky ways to look at the uninitialised variable before or after.)
ok. but why is this a better method? the throw new Error() line will never be reached and is just there to placate the compiler. I don't see the advantage of this answer over the one I posted and Ryan Elkin's - let me know if you think there's something I'm missing
@froadie I would argue it is better because there is less noise (1 line vs 2) and the noise is restricted to the error handling code.
4

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...

7 Comments

That's ugly - you don't want the = "".
No, not null either. IT SHOULD NOT BE ASSIGNED.
@Tom Hawtin: You have to initialize fileName to something. If you don't you would get The local variable fileName may not have been initialized compile error. null is definitely a good choice.
@Alexander Pogrebnyak No you don't. Not if you definitely do not read the variable again.
@Tom Hawtin: Have to initialize variable to NULL for the same token your fileName() function has to throw Error( ) after System.exit( 1). For compiler System.exit is just another function and it needs a special terminator in the block. Granted, you could put fileName = null; after System.exit instead of initializing it at the declaration line. BTW, I do think that your solution is cleaner.
|
1

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...?

1 Comment

-1 This does not even attempt to answer the OPs actual question.
0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.