I am quite convinced that here
final int i;
try { i = calculateIndex(); }
catch (Exception e) { i = 1; }
i cannot possibly have
You are correct that if the assignment is the very last operation in the try block, we know that upon entering the catch block the variable will not have been assigned. However, formalizing the notion of "very last operation" would add significant complexity to the spec. Consider:
try {
foo = bar();
if (foo) {
i = 4;
} else {
i = 7;
}
}
Would that feature be useful? I don't think so, because a final variable must be assigned exactly once, not at most once. In your case, the variable would be unassigned if an Error is thrown. You may not care about that if the variable runs out of scope anyway, but such is not always the case (there could be another catch block catching the Error, in the same or a surrounding try statement). For instance, consider:
final int i;
try {
try {
i = foo();
} catch (Exception e) {
bar();
i = 1;
}
} catch (Throwable t) {
i = 0;
}
That is correct, but wouldn't be if the call to bar() occured after assigning i (such as in the finally clause), or we use a try-with-resources statement with a resource whose close method throws an exception.
Accounting for that would add even more complexity to the spec.
Finally, there is a simple work around:
final int i = calculateIndex();
and
int calculateIndex() {
try {
// calculate it
return calculatedIndex;
} catch (Exception e) {
return 0;
}
}
that makes it obvious that i is assigned.
In short, I think that adding this feature would add significant complexity to the spec for little benefit.