Chris Uppal wrote On 12/12/05 16:02,:
Eric Sosman wrote:
Just curious. If the Java language had a non-local-return feature, with
appropriate syntax, and semantics that was (at worst) no harder to get right
than throwing an exception (i.e. /not/ C's setjmp()/longjmp()), would you still
feel the same way ?
Is the issue the "shape" of the control-flow or the syntax used to express it ?
(For me it's the latter -- I don't like the idiom either, but that's because I
would expect that people reading the code would /not/ expect exceptions to be
used in that way; I have no problems with a precisely controlled, but
non-local, flow of control in itself. Which, come to think of it, is just as
well since I don't object to exceptions...)
The syntax doesn't bother me much, although there's a
certain amount of clumsiness about try/catch/finally. The
fact that each of the three blocks has its own scope means
that there's no single scope for the entire construct; this
makes me declare variables at a scope wider than they really
ought to have, or introduce an extra level of { } simply to
create a containing scope. And it's irritating to catch an
IOException, say, and then put yet another try/catch inside
the finally that closes the BufferedReader or whatever. But
these are just cosmetic issues, nothing I can't live with.
Nor is the shape of the control flow a problem. The route
taken by a Java exception has the right balance of unpredictable
(the thrower doesn't know the catcher) and predictable (the
catcher will necessarily be an active call frame). A goto style
(e.g., PL/I or Pascal) loses the flexibility of "anonymous" (or
else requires label variables, which are hard to validate), while
the trust-the-programmer style of C's longjmp() all too often
engenders really horrible bugs. Java exceptions unwind in a
calm and orderly fashion, which I find soothing.
My objections to control-flow-by-exception are twofold.
First, there's efficiency. Yes, I know it's unfashionable
to talk about efficiency, but those of us who programmed in
Olden Times formed the hard-to-shake habit of keeping it in
mind. I'm still an efficiency addict (on, er, method-done
treatment ;-), and cannot help but think of all the cycles
that must be spent on a throw/catch. Even if you pre-construct
the thrown exception, the JVM still needs to conduct a possibly
lengthy search up the stack to find candidate catch blocks; a
chain of returns -- even if a method result must be if'ed at
each level -- will almost certainly be faster.
My other objection is a "right job, wrong tool" feeling.
Sure, it's possible to use exceptions for control flow, but
Java's control flow constructs are there because they're more
convenient, more readable, and better suited to the task. One
can replace a "switch" with a try/catch/catch/catch/catch...
block, but that doesn't mean one ought to. Exceptions should
be, well, exceptional -- they're not called Normals, after all!
I like to think of a method as having a contract that in normal
circumstances is "contained" in its arguments, its returned
value, and the states of the objects it touches. When I call
the method, I want it to return with its contract fulfilled.
Exceptions are for when there's something that prevents the
method from fulfilling its contract: invalid arguments or state,
I/O errors, whatever.
So: My objections amount to an old-fashioned concern for
efficiency combined with a Puritanical fastidiousness. Neither
is binding on anyone else; follow your weird.