92

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

dtech
  • 13,741
  • 11
  • 48
  • 73
  • 1
    I doubt you can do this without a temporary variable. – NPE Nov 28 '12 at 11:35
  • 11
    `final int x = makeX();` definitely. (try-catch in function) – Joop Eggen Nov 28 '12 at 11:36
  • 2
    Shocking that the JDK [still doesn't have a `tryParse`](http://stackoverflow.com/questions/1486077/java-good-way-to-encapsulate-integer-parseint). – T.J. Crowder Nov 28 '12 at 11:41
  • @T.J.Crowder definitly, but it is irrelevant in this case since I just used the integer as an example – dtech Nov 28 '12 at 11:43
  • 12
    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. – jaco0646 Oct 22 '14 at 19:41
  • 4
    @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. – Joshua Goldberg Jun 10 '16 at 17:26
  • I guess the real problem is that Java's `try`-`catch` statement does not support an `else` case like Python. – augurar Dec 05 '16 at 10:53
  • Kotlin has try expressions for this (https://kotlinlang.org/docs/reference/exceptions.html#try-is-an-expression). Hopefully they'll be added to Java some day. – Frans Oct 09 '20 at 13:10
  • Why does it give the compiler error? Either an exception will be thrown and the value will be set in the catch block or the method will return a value successfully. Is it just the way the compiler is designed for try-catch or is there something I am missing here? – humbleCoder Dec 30 '22 at 07:34

2 Answers2

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 move both branches of the code into a function:

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 a 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;
  }
}
NPE
  • 486,780
  • 108
  • 951
  • 1,012
  • For a local scope I agree with you, however this most often occurs with instance variables. – dtech Nov 28 '12 at 11:45
  • 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 – thar45 Nov 28 '12 at 11:45
  • 1
    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. – Joshua Goldberg Jun 10 '16 at 17:37
  • 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 " may not have been initialized". The lack of control-flow analysis for this situation is frustrating, but easily worked around. – Ti Strga Nov 15 '18 at 17:03
-1

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.

SomeJavaGuy
  • 7,307
  • 2
  • 21
  • 33
  • 17
    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. – Stefan Feb 18 '14 at 17:43
  • To support what @Stefan said, Clang is able to figure this out when compiling Swift. – Franklin Yu Apr 29 '16 at 02:25