Surprising at first? Maybe. Cursed? Wouldn't say so. It is merely unconventional use of the construct.
The preceding descriptions say "attempts to transfer control" rather than just "transfers control" because if there are any try statements (§14.20) within the method or constructor whose try blocks or catch clauses contain the return statement, then any finally clauses of those try statements will be executed, in order, innermost to outermost, before control is transferred to the invoker of the method or constructor. Abrupt completion of a finally clause can disrupt the transfer of control initiated by a return statement.
This, I believe, is the only way for "return E", after the evaluation of E completes normally, to not return the E's value to the caller. Thanks for a needless corner-case complication, I guess.Yes, that's what exceptions and their handling handling does - it messes with control flow.
> The conventional intuition is that right after a "return" statement completes, the current method's invocation ends and the control returns to the caller.
It is part of similar conventional thinking that "a method call must return back to the caller on termination", which is not even applicable to Java and other languages with conventional exceptions.
> Thanks for a needless corner-case complication, I guess.
But what is the alternative otherwise? Not execute finally block if try block exits normally? Execute finally block, but ignore control flow statements? What if code in finally block throws? I maintain that execution of finally block, including control flow statements within, normally is the only sane behavior.
I think there three distinct classes of surprising that fall under this umbrella: 1. a behavior overrides another behavior due to some precedence rules 2. behavior of a construct in language x is different than behavior of similar construct in most mainstream languages 3. who in their right mind would design the thing to behave like this.
Examples could be: string "arithmetic" in most dynamically typed languages, PHP's ternary operator order of precedence and Python's handling of default function arguments. In all of these cases when you sit down, think about it and ask yourself "what's the alternative?", there's always a very reasonable answer to it, therefore, I think it is reasonable to call these behaviors cursed.
In this case the surprise factor comes in, because we are used to equating finally block with cleanup and I concur that many would trip on this the first time. But if you gave this exercise some time and asked "what should happen if finally block contains control flow statements?" the reasonable answer should be "take precedence", because the behavior would be surprising/cursed otherwise.
That's my reasoning.