Home > Archive > Java Help > February 2007 > Re: Why does Java require the throws clause? Good or bad language design? (fwd)
You are viewing an archived Text-only version of the thread.
To view this thread in it's original format and/or if you want to reply to
this thread please [click here]
| Author |
Re: Why does Java require the throws clause? Good or bad language design? (fwd)
|
|
| Arthur J. O'Dwyer 2007-02-19, 10:06 pm |
|
Sorry for the double-post, comp.lang.java.* readers. I accidentally
cced "comp.misc" instead of "comp.lang.misc" the first time, and then
spent a lot of time wondering why the message didn't show up...!
Repost:
On Sun, 18 Feb 2007, James Harris wrote:
[...][color=darkred]
> My main objections to Java's way is that if aa calls bb calls cc ...
> calls zz and I want to handle zz's errors in aa I still have to either
> declare them in all intervening modules or catch a generic Exception
> superclass and possibly encapsulate them in another object. The latter
> is a kludge, IMHO. The former unfortunately obscures the program logic
> and intention with foreign constructs or "mechanism."
Leaving aside the whole "does Java suck?" issue, ;) could some
Java expert please comment on the following scenario, which James'
scenario made me think of?
In C++, we can write
void higherLevel(void (*cbf)(int), int arr[], int n) {
for (int i=0; i < n; ++i)
cbf(arr[i]);
}
void callbackFunc(int x) {
if (x == 42)
throw "random exception";
}
void catcher() {
int arr[5] = {1,2,3,42,5};
try {
higherLevel(callbackFunc, arr, 5);
} catch (char *e) {
puts("Exception was thrown:");
puts(e);
}
}
How would the same thing be written in Java? Notice that the
function 'higherLevel' does not know (or need to know) anything
about the exception specification of 'callbackFunc'; I claim that
this is good design, because it means that 'higherLevel' can be
reused in other contexts.
The "contract" here is between 'catcher' and 'callbackFunc';
that is, 'catcher' should catch whatever 'callbackFunc' throws.
But 'higherLevel' is a neutral party in all this; it shouldn't
concern itself with those details.
C++ doesn't let us express that contract very well, unfortunately,
but it does let us write 'higherLevel' without the baggage. Does
Java?
(IIRC, in Java you'd use a bunch of classes or interfaces instead
of bare functions, but I hope you get the idea.)
And a topic for the c.l.misc crowd: What's the Right Way to
handle this scenario? Does C++ really get it right? How do real
functional languages do it?
-Arthur
| |
| Chris Smith 2007-02-19, 10:06 pm |
| Arthur J. O'Dwyer <ajonospam@andrew.cmu.edu> wrote:
> Leaving aside the whole "does Java suck?" issue, ;) could some
> Java expert please comment on the following scenario, which James'
> scenario made me think of?
Yes, I can comment. The scenario is ugly to handle in Java.
> How would the same thing be written in Java?
The best that can be done is to wrap exceptions from the callback
function in some exception class designed for that purpose, and then let
catcher handle unpacking the original exception and rethrowing it. The
result looks something like this:
public class CallbackException extends Exception
{
CallbackException(Throwable e) { super(e); }
}
public interface Callback
{
public void run(int arg) throws CallbackException;
}
public class CallbackImpl implements Callback
{
public void run(int x) throws CallbackException
{
if (x == "42") throw new RandomException();
}
}
public void higherLevel(Callback cbf, int[] arr)
throws CallbackException
{
...
}
public void catcher()
{
try
{
higherLevel(new CallbackImpl(),
new int[] { 1, 2, 3, 42, 5 });
}
catch (CallbackException e)
{
if (e.getCause() instanceof RandomException)
{
... handle exception ...
}
}
}
> Notice that the
> function 'higherLevel' does not know (or need to know) anything
> about the exception specification of 'callbackFunc'; I claim that
> this is good design, because it means that 'higherLevel' can be
> reused in other contexts.
I agree that it is good design, but it is not handled well in Java.
That is one of the possible improvements I mentioned elsethread.
> And a topic for the c.l.misc crowd: What's the Right Way to
> handle this scenario? Does C++ really get it right? How do real
> functional languages do it?
I'm not part of the c.l.misc crowd, but here goes.
The right way to do it, if you want static validation, is obviously as
follows: you type higherLevel as being a function that, given an
exception list, takes as parameters a function which throws that
exception list, and an array, and itself throws the same exception list.
Abusing Haskell syntax for the moment, the type might be something like:
(Int -> MightFail e ()) -> [Int] -> MightFail e ()
That is, when attempting to type higherLevel, the exception list (e)
ought to be transparently passed through from the first parameter. The
higherLevel function would automatically pick up the exception type from
its first parameter.
Continuing along the Haskell direction, it's not difficult to build a
Monad in Haskell to handle the problem exactly as given above. The
monad declaration for MightFail looks something like:
data MightFail e a = Failed e | Success a deriving Show
instance Monad (MightFail e) where
(Success x) >>= k = k x
(Failed y) >>= _ = Failed y
(Success _) >> k = k
(Failed y) >> _ = Failed y
return = Success
throw :: e -> MightFail e a
throw = Failed
(This is actually creating the exception handling system; so it's
something that could conceivably be built into some other language; it
shouldn't fairly be counted as part of the code versus the C++ code
above.) The declaration of higherLevel looks like this:
higherLevel :: (Int -> MightFail e ())
-> [Int]
-> MightFail e ()
higherLevel f [] = return ()
higherLevel f (i:is) = do f i
higherLevel f is
Finally, the following code declares a few exception types, and
implements the callback function and catcher.
data Exception = FirstException | SecondException | ThirdException
callbackFunc :: Int -> MightFail Exception ()
callbackFunc 42 = throw SecondException
callbackFunc _ = return ()
catcher :: IO ()
catcher = do let r = higherLevel callbackFunc [1, 2, 3, 42, 5]
case r of
Success _ -> return ()
Failed FirstException -> putStrLn "first exception"
Failed SecondException -> putStrLn "second exception"
Failed ThirdException -> putStrLn "third exception"
Hope that's something like what you wanted to see, 'cause it took me a
while ;). Of course, you asked about functional languages in general.
This is one functional language. In non-pure functional languages,
you'd be likely to have some kind of ad hoc exception system built in,
much like Java or C++ does.
It's also worth pointing out that this code does not actually fail to
compile if you don't handle all possible exceptions; so in a sense, it
misses the originally stated goal. However, most Haskell compilers have
an option to give a warning if you miss something in pattern matching.
It's up to you to turn it on or off.
--
Chris Smith
| |
| Chris Smith 2007-02-20, 8:07 am |
| Lew <lew@nospam.lewscanon.com> wrote:
> Arthur J. O'Dwyer wrote:
>
> Chris Smith wrote:
>
> This question was handled with an extremely non-ugly solution by Michael
> Rauscher, I believe it was.
Well, obviously Michael Rauscher's solution wasn't even a solution. The
higherLevel method either knows something about the error conditions
that might occur in callbackFunc, OR you have to give up on static
validation entirely. Either choice dodges the issue. The only way for
higherLevel to carry the exception back to catcher without knowing
something about the exception type is to wrap the exception into
something that higherLevel does know about. Of course, the C++ version
is equally ugly, because it gives up that kind of validation by default.
> The solution you showed is similar and not ugly either.
I certainly think it's at least a little ugly, given the situation. I
don't object to wrapping exceptions in general; but in this case, it's
just mechanism to allow the exceptions to be carried through a method
that shouldn't know about them.
The only elegant solution that I've seen (and I mean real solutions
here) is the one in Haskell, which allows higherLevel to have exactly
the type it should: namely, that there is some set of exceptions e, that
that the callback parameter and higherLevel must both have e as their
set of error conditions.
--
Chris Smith
| |
| Bobsparks@excite.com 2007-02-20, 7:07 pm |
| I believe the main rationale after the portability, behind java is to
protect programmers from themselves and other programmers. The
original "C" style contract is easy to break or worse forget.
Therefore Java enforces you to at least catch any exception as a
generic Exception. I usually just Catch everything as an Exception
later if the business rules require two sorts of handling, IE inform
the user they must not supply zero or the DBA admin the database is
full then you can always use instanceof to cast back to some specific
behaviour. Original developers dream up all kinds of custom
exceptions usually though all it means is you are screwed and need to
log a message. Thankfully every exception can be cast to an Exception
and the reported with toString.
| |
| Robbert Haarman 2007-02-20, 7:07 pm |
| On Tue, Feb 20, 2007 at 09:23:16AM -0500, Lew wrote:
>
> It is a bit sloppy to catch Exception instead of the particular checked
> exceptions that a method can throw.
Not if you want to handle all exceptions the same way, right?
Catching generic exceptions and then using instanceof or downcasts is,
IMO, ugly. Is that what you meant, as well?
Regards,
Bob
--
"UNIX was not designed to stop you from doing stupid things, because that
would also stop you from doing clever things."
--Doug Gwyn
| |
| Chris Smith 2007-02-20, 7:07 pm |
| Lew <lew@nospam.lewscanon.com> wrote:
> Do you think we could put this thread back in with the original one on this
> topic? I am slightly befuddled by having to refer to a different thread to
> find the antecedents.
I don't know what you're proposing. You have a problem with the
References headers? If so, change them however you like in your
replies.
--
Chris Smith
| |
| Karl Uppiano 2007-02-20, 10:06 pm |
| If you want to throw an exception through a method that knows nothing about
it, the lower level function can always throw a RuntimeException, or a
specialized derivative of it. Since a RuntimeException is un-checked, you
don't have to declare it. But the higher level function can catch
RuntimeExceptions if it wants to. It can even catch specific types if it
knows about them in advance.
| |
| Karl Uppiano 2007-02-20, 10:06 pm |
|
"Karl Uppiano" <karl.uppiano@verizon.net> wrote in message
news:pXOCh.4919$lG6.3803@trndny08...
> If you want to throw an exception through a method that knows nothing
> about it, the lower level function can always throw a RuntimeException, or
> a specialized derivative of it. Since a RuntimeException is un-checked,
> you don't have to declare it. But the higher level function can catch
> RuntimeExceptions if it wants to. It can even catch specific types if it
> knows about them in advance.
And another thing: I always wished they had made InterruptedException a
RuntimeException. Hardly anyone cares about that exception, and most of the
time, when someone wants a thread to sleep or wait, they just eat
InterruptedException in an empty catch block, which is just wrong. I often
wrap it in a RuntimeException and throw that instead, so higher-ups can deal
with it without every layer having to declare it or catch it. I wonder how
many unnecessary lines of code have been written to deal with
InterruptedException.
| |
| Robbert Haarman 2007-02-21, 4:10 am |
| On Wed, Feb 21, 2007 at 04:04:27AM +0000, Karl Uppiano wrote:
>
> And another thing: I always wished they had made InterruptedException a
> RuntimeException. Hardly anyone cares about that exception, and most of the
> time, when someone wants a thread to sleep or wait, they just eat
> InterruptedException in an empty catch block, which is just wrong.
Doesn't the exception (also) signal that Thread.sleep (or whatever) did
not sleep for the whole interval that was specified? In that case,
simply catching and ignoring it is not enough.
> I often wrap it in a RuntimeException and throw that instead, so
> higher-ups can deal with it without every layer having to declare it
> or catch it.
Wouldn't it abort the program if the exception isn't caught and handled?
> I wonder how many unnecessary lines of code have been
> written to deal with InterruptedException.
What should have happened (and I think it must have happened, too), is
the implementation of wrappers that make the exception go away. E.g., in
case of Thread.sleep, the wrapper would sleep for the specified amount
of time, regardless of how many times the actual Thread.sleep got
interrupted.
Regards,
Bob
--
You are in a maze of twisted little characters, all different.
-- Me, while learning Chinese
| |
| Karl Uppiano 2007-02-21, 4:10 am |
|
"Robbert Haarman" <comp.lang.misc@inglorion.net> wrote in message
news:20070221060137.GZ3715@morgenes.shire.sytes.net...
> On Wed, Feb 21, 2007 at 04:04:27AM +0000, Karl Uppiano wrote:
>
> Doesn't the exception (also) signal that Thread.sleep (or whatever) did
> not sleep for the whole interval that was specified? In that case,
> simply catching and ignoring it is not enough.
The main reason for the InterruptedException is that someone might interrupt
the thread, wanting it back, or wanting it to end. The person holding the
reference to the thread might not be the person making it wait. By catching
and eating the InterruptedException, they can prevent the thread from
returning control to the person interrupting it, which is probably not the
desired behavior. If it were a RuntimeException, they would not be compelled
to handle the exception, but the person interrupting the thread would expect
it, and be prepared to deal with it. They'd have to catch it so that the
thread would not die, or they could let it fly, and the system would kill
the thread, which might be exactly what they want.
>
> Wouldn't it abort the program if the exception isn't caught and handled?
Only that thread would die, unless it was the or only thread in the
application. Usually, though, it is *me* catching the exception way up in
the vicinity of main(), and doing something specific with it.
>
> What should have happened (and I think it must have happened, too), is
> the implementation of wrappers that make the exception go away. E.g., in
> case of Thread.sleep, the wrapper would sleep for the specified amount
> of time, regardless of how many times the actual Thread.sleep got
> interrupted.
That was not the intent of InterruptedException. It was really intended as a
way to forcibly break out of a wait. The exception provides a good way to
detect that condition. I only wish it were a RuntimeException, so that if I
don't expect it (e.g., I have the only reference to the thread and I'm
making it wait, therefore it cannot be interrupted by anyone else), I would
not have to explicitly handle it, because it should never happen.
> Regards,
>
> Bob
>
> --
> You are in a maze of twisted little characters, all different.
> -- Me, while learning Chinese
>
| |
| Chris Smith 2007-02-21, 7:16 pm |
| Michael Rauscher <michlmann@gmx.de> wrote:
> The question was how a given C++ code can be expressed in Java. My
> response hopefully answered this question.
Yes, I suppose it did; in that neither Java nor C++ has an acceptable
solution to that problem. I understood the question to be addressing
how static validation of error handling works in such a scenario.
--
Chris Smith
|
|
|
|
|