Re: Why does Java require the throws clause? Good or bad language design?
On Tue, Feb 20, 2007 at 01:04:11PM -0500, Arthur J. O'Dwyer wrote:
On Tue, 20 Feb 2007, Michael Rauscher wrote:
Arthur J. O'Dwyer wrote:
On Mon, 19 Feb 2007, Michael Rauscher wrote:
catcher calls higherLevel, so one contract is between catcher and
higherLevel.
higherLevel calls cbf, so there's another contract: between
higherLevel and cbf.
FWIW, you're wrong. The "contract" is obviously between the main
function 'catcher' and its helper function 'callbackFunc'. Now, I'll
agree that Java does not let us /express/ that contract; but that's
not the same thing as its not existing in the first place.
You can't just ignore the higherLevel method.
I /want/ to ignore the higherLevel method! Here's an analogy that
I think will be extremely clear to Lisp programmers, but maybe not to
C++/Java programmers: I can write
for (...) {
callbackFunc();
}
instead of
for (...) throws Exception {
callbackFunc();
}
Exceptions thrown by the inner callbackFunc() are quietly and
transparently passed out of the loop and up to the surrounding
context. So why can't I create my own "control structures" by
writing
... higherLevel(...) { ... }
higherLevel(callbackFunc);
instead of
... higherLevel(...) throws Exception { ... }
higherLevel(callbackFunc);
Meh, having to write "throws Exception" when you define higherLevel is
not nearly as bad as having to write "throws Exception" each time you
write a for loop.
Having said that, I completely agree that it would be nice if higher
order functions would simply pass along the exceptions from callbacks
passed to them. And of course, removing the mandatory throws clause
would accomplish just that. Unfortunately, that doesn't even begin to
solve the ugliness of higher order functions in Java...
So let's make it
public void higherLevel( CBF cbf, int arr[] ) throws Exception {
for (int i=0; i < arr.length; ++i)
cbf.execute(arr[i]);
}
Now I see two problems, but one of them might be a mirage. That one
is: what happens if we want to use higherLevel() with a callback
function that might throw something /else/? We'd have to make it
public void higherLevel( CBF cbf, int arr[] )
throws Exception, SomethingElse {
for (int i=0; i < arr.length; ++i)
cbf.execute(arr[i]);
}
No. SomethingElse will either not be a checked exception, which means it
does not have to appear in the throws clause, or it will be a subclass
of Exception, which means it's already covered.
However, you _would_ have to use instanceof or upcasts to get back from
Exception to the actual type, if necessary. If you write
someMethod(...) throws Exception {
...
}
try {
someMethod(...);
catch(IOException ioe) {
...
}
The compiler will complain that someMethod does not throw IOException,
even if the body of someMethod does.
The more serious problem is, what happens if catcher2() wants to
perform higherLevel() on callback2(), and this time the contract
between the *2() functions does /not/ involve any exceptions being
thrown?
interface CBF {
public void execute(int x); // "throws Exception" or not, doesn't matter
}
class Foo2 {
// This is the same higher-level library function, reused.
// Pretend it's in a different translation unit.
public void higherLevel( CBF cbf, int arr[] ) throws Exception {
for (int i=0; i < arr.length; ++i)
cbf.execute(arr[i]);
}
public void catcher2()
{
int arr[] = new int[] { 1, 2, 3, 42, 5 };
CBF callBack2 = new CBF() {
public void execute(int x) {
return; // no exceptions here!
}
};
higherLevel( callBack2, arr );
// no need for a catch; callBack2 doesn't throw
}
}
There's no way to make this code work, unless you are willing to do
one of three things:
(1) Change the library; i.e., give up on code reuse.
(2) Add spurious "catch" clauses to every use of 'higherLevel',
thus confusing the reader, since exceptions aren't being used in
this code.
(3) Add spurious "throws Exception" clauses to every function
in the program and a "catch" in main, again confusing the reader,
but at least conserving the vertical space.
I am starting to think that the best way to write higher order functions
in Java would be to have the higher order function catch exceptions from
the callback and wrap them in some sort of unchecked exception, leaving
it up to the caller (of the higher order method) whether these
exceptions will be handled or not.
On second thought, even this solution isn't satisfactory, because now
(1) the compiler won't enforce checked exceptions anymore, and (2) the
nested exceptions have to be unboxed, and getting back to the original
exception classes will require instanceof/upcast/whatever ugliness.
Arggh...
Of course, I can't give Java solutions to 'problems' that are not
feasible using Java. So, if the question had been if it's possible to
pass checked exceptions through methods without declaring them, then
I'd answered: no ;)
Fair enough. Of course, since we started out discussing language
_design_, the question to ask at this point is "should it be possible"?
I think the case for "yes" has been made, by demonstrating that
functional programming becomes ugly otherwise. But then again, it's
already clear that Java wasn't designed for functional programming, so
perhaps a bit more ugliness isn't a concern.
Regards,
Bob
--
Furious activity is no substitute for understanding.
-- H.H. Williams