97

Because I believe it is a good programming practice, I make all my (local or instance) variables final if they are intended to be written only once.

However, I notice that when a variable assignment can throw an exception you cannot make said variable final:

final int x;
try {
    x = Integer.parseInt("someinput");
}
catch(NumberFormatException e) {
    x = 42;  // Compiler error: The final local variable x may already have been assigned
}

Is there a way to do this without resorting to a temporary variable? (or is this not the right place for a final modifier?)

9
  • 1
    I doubt you can do this without a temporary variable. Commented Nov 28, 2012 at 11:35
  • 11
    final int x = makeX(); definitely. (try-catch in function) Commented Nov 28, 2012 at 11:36
  • 2
    Shocking that the JDK still doesn't have a tryParse. Commented Nov 28, 2012 at 11:41
  • 13
    To be perfectly clear, the compiler error is incorrect, is it not? There is no circumstance under which x could be assigned twice in the given example. Commented Oct 22, 2014 at 19:41
  • 5
    @jaco0646, it's asking a lot for the compiler to get that in general when there are multiple lines in the try block where the exception might happen. It would be nice to have an exceptional case for this purpose, though, detecting when the assignment is the last statement in the try. Commented Jun 10, 2016 at 17:26

3 Answers 3

81

One way to do this is by introducing a (non-final) temporary variable, but you said you didn't want to do that.

Another way is to create a function for both branches of the code:

final int x = getValue();

private int getValue() {
  try {
    return Integer.parseInt("someinput");
  }
  catch(NumberFormatException e) {
    return 42;
  }
}

Whether or not this is practical depends on the exact use case.

All in all, as long as x is an appropriately-scoped local variable, the most practical general approach might be to leave it non-final.

If, on the other hand, x is a member variable, my advice would be to use a non-final temporary during initialization:

public class C {
  private final int x;
  public C() {
    int x_val;
    try {
      x_val = Integer.parseInt("someinput");
    }
    catch(NumberFormatException e) {
      x_val = 42;
    }
    this.x = x_val;
  }
}
Sign up to request clarification or add additional context in comments.

4 Comments

For a local scope I agree with you, however this most often occurs with instance variables.
I guess it could reflect an error cannot make a static reference to the non-static method getValue(), so we are suppose to use the static function ,, i may be wrong private static int getValue() ..@NPE
If this.x is an object-type like Integer, then you need a little more (sadly). If you leave x_val undeclared, the compiler will complain that it may not have been initialized. If the fall-back for the catch block is null, you need to pre-initialize to null and either redundantly assign null in the catch for clarity (that's my preference) or have an empty catch.
What @JoshuaGoldberg says is true even for primitive types. We have exactly the same pattern of code, where the member in the role of this.x is a primitive boolean, as is the local variable. Even with Java 9, we get "<thing_in_the_role_of_x_val> may not have been initialized". The lack of control-flow analysis for this situation is frustrating, but easily worked around.
1

One way to achieve this with Java Optional type:

public void f() {
    final int a = Optional.<Integer>empty().orElseGet(() -> {
        try {
          return operationCanThrow();
        } catch(final Exception e) {
          return 42;
        }
    });
}

Comments

0

No it is not the right place, imagine you got more then 1 Statement in your try and catch block, the first one says : x = 42. After some others Statements the try block fails, and it goes to the catch block, where your Saying x = 30. Now you defined x twice.

2 Comments

The compiler is smart enough to know which statements throw which exceptions. It may not be possible in all cases but just like the compiler can tell you in some cases about dead code etc. it should be able to figure out if final would work.
To support what @Stefan said, Clang is able to figure this out when compiling Swift.

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.