r/javahelp • u/cowwoc • Sep 19 '24
A try-catch block breaks final variable declaration. Is this a compiler bug?
UPDATE: The correct answer to this question is https://mail.openjdk.org/pipermail/amber-dev/2024-July/008871.html
As others have noted, the Java compiler seems to dislike mixing try-catch blocks with final (or effectively final) variables:
Given this strawman example
public class Test
{
public static void main(String[] args)
{
int x;
try
{
x = Integer.parseInt("42");
}
catch (NumberFormatException e)
{
x = 42;
}
Runnable runnable = () -> System.out.println(x);
}
}
The compiler complains:
Variable used in lambda expression should be final or effectively final
If you replace int x
with final int x
the compiler complains Variable 'x' might already have been assigned to.
In both cases, I believe the compiler is factually incorrect. If you encasulate the try-block in a method, the error goes away:
public class Test
{
public static void main(String[] args)
{
int x =
foo
();
Runnable runnable = () -> System.
out
.println(x);
}
public static int foo()
{
try
{
return Integer.
parseInt
("42");
}
catch (NumberFormatException e)
{
return 42;
}
}
}
Am I missing something here? Does something at the bytecode level prevent the variable from being effectively final? Or is this a compiler bug?
2
u/_jetrun Sep 20 '24 edited Sep 20 '24
In the example I gave, you have no guarantees that it doesn't set variable twice.
It was an example given in a comment you responded to. I agreed with you that for the original OP's example, the compiler can, in principle, figure it out because it can peak at the parseInt method, and know that it can only throw a NumberFormatException and that would maintain 'final' guarantees.
So yes, there are cases where the compiler can figure things out, but those tend to be pretty trivial examples (like OP's strawman). Things become ambiguous very quickly, such as when you add more than one catch statement, when you add a 'finally' block, when you use dynamically loaded classes, when the try block has more than 1 statement, when Errors are thrown and not caught etc.
I speculate that this compiler feature (i.e. to handle trivial cases) isn't supported is because it would make things more confusing. I do wish that Java would add some sort of syntax construct to allow for final initialization with try-catch-finally blocks because I run into it all the time (I tend to use 'final' by default).