views:

359

answers:

4

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

+2  A: 

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

froadie
That's ugly - you don't want the `= ""`.
Tom Hawtin - tackline
@Tom Hawtin - true. It should probably be initialized to null just from an ideal design perspective. But practically, it could be initialized to just about anything, as it's either going to be immediately replaced or the program will crash. But I'll change it just to be correct
froadie
No, not `null` either. IT SHOULD NOT BE ASSIGNED.
Tom Hawtin - tackline
@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
@Alexander Pogrebnyak No you don't. Not if you definitely do not read the variable again.
Tom Hawtin - tackline
@Tom: You WILL get a compiler error if you just change it to 'String fileName;'. That variable has to be initialized to SOMETHING for this example to compile. It sounds like you're talking about a different solution rather than just not initializing the temp variable.
Ryan Elkins
@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.
Alexander Pogrebnyak
+7  A: 

How about

String tempName = null;
try{
   tempName = buildFileName();
}
catch(Exception e){
   ...
   System.exit(1);
}
FILE_NAME = tempName;
Ryan Elkins
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
froadie
Heh, yeah. I think you beat me to it by just a few seconds ;)
Ryan Elkins
+3  A: 

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.

Tom Hawtin - tackline
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
@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.)
Tom Hawtin - tackline
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
@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.
ILMTitan
A: 

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

Neil Coffey