2 ms·
It's because if you had final Foo foo; try { foo = bar(baz); otherFunction(); } catch (SomeException ex) { foo = SOME_DEFAU
by dzaima 4y ago
It's because if you had
final Foo foo;
try {
foo = bar(baz);
otherFunction();
} catch (SomeException ex) {
foo = SOME_DEFAULT;
}
that'd truly be invalid - the first assignment could run, otherFunction errors, and foo gets reassigned. Even if SomeException is a checked exception that otherFunction doesn't declare in its throws, it's still possible to get the jvm to throw it, and things do rely on that - see Lombok @SneakyThrows.
So the only case that such final field assignment can work is if it's the last evaluated expression in the try case (plus some guaranteed non-throwing arithmetic or boring assignments), which is quite the weird thing to have as specified behavior in a language.
- vbezhenar 4y agoWhat's non-boring assignment? Every assignment in Java is boring. This is just bad flow analyzer and that's about it.
- dzaima 4y agosomething like final Foo foo; try { foo = bar(baz); Integer a = null; int b = a; } catch ... (though that's less the assignment, more implicit behavior; point being, you've got quite the restrictive environment after "foo =")
- vbezhenar 4y agoAccepting that kind of code probably is too much to ask. But accepting original example should be do-able with pretty simple AST pattern matching.
- dzaima 4y agoRight. Not saying that it'd be hard to support the original code, but more that it's just an extremely weird case to have. As in, I'd say that adding "foo = try { bar(baz) } catch (SomeException ex) { SOME_DEFAULT }", even though that requires adding the ability for blocks to "return" a value, would be a more sensible change to aid the problem at hand. Were the original to be allowed, it be the first case where adding a System.out.println(); statement into code {}s results in an error. (ok, second case, the first being adding it before a super()/this() in a constructor. But hey look what post we're on!)
- layer8 4y agoOf course. But Java could recognize if any potentially-throwing code is executed within the try block after the assignment, similar to how it tracks where checked exceptions are being thrown. Meaning, when needed you could still always do: final Foo foo; try { Foo tmpFoo = bar(baz); otherFunction(); foo = tmpFoo; } catch (SomeException ex) { foo = SOME_DEFAULT; } But at present the "best" (besides introducing a separate method) you can do is final Foo foo; Foo tmpFoo; try { tmpFoo = bar(baz); otherFunction(); } catch (SomeException ex) { tmpFoo = SOME_DEFAULT; } foo = tmpFoo; Which has the drawbacks that (a) the compiler doesn't check that tmpFoo is assigned exactly once (because you can't make it final), and (b) tmpFoo stays around for the rest of the scope, with the risk of being misused later. I would understand if an excemption is being made for Errors, because in princible any JVM bytecode instruction could probably throw one, but not for regular Exceptions.