Why does Java require the throws clause? Good or bad language design?

L

Lew

Robbert said:
It seems you have completely missed my point. When you are the one
writing all the code, of course you get to decide which exceptions are
checked exceptions and which aren't, in current Java.

The issue I have been trying to point out is that there is value in
being able to decide this _even when you are not writing all the code_.
For example, that, for this snippet of code, you want OutOfMemoryError
to be a checked exception, and or that for that program, you want
IOException to be an unchecked exception.

Current Java (AFAIK) does not allow you to make that decision.

And that is a good thing.

You are saying you want to override another programmer's decision on how their
API should be called. They declare a checked exception and you want to treat
it like an unchecked one. That defeats the whole purpose of them declaring a
checked exception in their API.

Of course, you can always *handle* the exception the other programmer demanded
that you handle. You can always rethrow it as an unchecked exception. But that
is not a flaw in Java. That is you disagreeing with the library writer.

- Lew
 
R

Robbert Haarman

And that is a good thing.

You are saying you want to override another programmer's decision on how
their API should be called.

No, I am overriding the decisions about how errors that occur should be
handled in _my_ code.
They declare a checked exception and you want to treat it like an
unchecked one. That defeats the whole purpose of them declaring a
checked exception in their API.

You are assuming there is such a purpose. I maintain that only I know
whether it makes sense for my code to handle an exception beyond
aborting the thread with an error message. Since we cannot seem to agree
on that, it is pointless to keep arguing the things that depend on it,
so I propose we simply call it a difference in taste and leave it at
that.

Regards,

Bob
 
C

Chris Uppal

Lew said:
Great idea. We could call the ones that your program must handle
explicitly "checked" exceptions, and the language could signal them by
deriving them from Exception and requiring them in the throws clause of a
method signature. The language could allow implicit exceptions to be
handled or trivially rethrown without a throws clause by deriving them
from RuntimeException, and we will call such cases "unchecked" exceptions.

That's an interesting idea. I think, though, that it has some fairly serious
flaws. You should experiment with it, and with variations, for a while before
baking anything into your language design.

Maybe even try to find another language where it's been tried before, and see
whether it worked there.

;-)


BTW: one problem with the idea, which I've only seen touched on glancingly in
this thread so far, is that /if/ you want to use exception class hierarchies to
express nested categories of exceptional conditions (in itself something of an
abuse of the class concept, albeit useful and natural); then your scheme won't
play properly with that. If an exception hierarchy is to make sense then you
can't use one hierarchy to express two orthogonal concepts. It would be far
better to make the "is checked" a per-class attribute which can be set
independently for each exception class. It might make sense to make "is
checked" be inherited by default (though I'm not convinced[*]), but if so then
subclasses must be able to override the checkedness to turn it on /or/ off.

-- chris

[*] The checkedness of an exception class would be an /extremely/ important
aspect of any design, and I suspect there'd be more value in forcing designers
to make explicit decisions on a per-case basis, than in the (undoubtedly real)
savings in coding effort.
 
C

Chris Smith

Chris Uppal said:
Take this case (different from the running examples elsethread only for
variety).

Actually, it is significantly different in very important ways from the
example with the callback function declared elsewhere.
outer()
{
try
{
aaa();
}
catch (MyException e)
{
// whatever
}
}

aaa() { bbb(); }
bbb() { inner(); }

inner()
throws MyException
{
// whatever
}

I contend that the contract is between the code in outer() and the code in
inner(), and that neither aaa() nor bbb() should be forced to be party to that
contract (although they often will be anyway).

I definitely disagree with this. It breaks abstractions. Since outer
doesn't know that it ever interacts with inner, it cannot possibly be
under any kind of contractual relationship with inner. The only piece
of code with which outer has any relatinship is aaa (and the code which
calls it, which is insignificant here). Therefore, if there is any hope
of providing the static validation at all while respecting abstraction,
there needs to be something about aaa to indicate that it can fail in
those ways.

It's also not clear how you define outer as the method that has the
contract with inner. Is it that outer contains the catch block? If I
remove that contract, would you deny that outer should be forced to be
party to the contract? Then we're back to the original problem; if no
one has a catch block, then who is the other party to the contract?

I agree that it would be nice if the throws clause for aaa and bbb could
be inferred in some cases, esp. if they are private methods. I would
insist that the contractual obligations between caller and callee still
exist; it's just that you don't have to explicitly declare them.
Maybe it's just a difference in terminology.

Nope, clearly not.
 
C

Chris Uppal

Chris Smith wrote:

[me:]
Actually, it is significantly different in very important ways from the
example with the callback function declared elsewhere.


I definitely disagree with this. It breaks abstractions. Since outer
doesn't know that it ever interacts with inner, it cannot possibly be
under any kind of contractual relationship with inner. The only piece
of code with which outer has any relatinship is aaa (and the code which
calls it, which is insignificant here). Therefore, if there is any hope
of providing the static validation at all while respecting abstraction,
there needs to be something about aaa to indicate that it can fail in
those ways.

Contrariwise, there is no reason why bbb() should expose its relationship with
inner() -- that is also breaking abstraction. Even more so for aaa(). In fact
it is complicating the relationship between aaa() and bbb() by introducing
irrelevant (to them) complications and conditions on how they fit together.

It's also not clear how you define outer as the method that has the
contract with inner. Is it that outer contains the catch block? If I
remove that contract, would you deny that outer should be forced to be
party to the contract? Then we're back to the original problem; if no
one has a catch block, then who is the other party to the contract?

I think we are still differing in terminology -- this time about "contract".
For me it is about assigning a certain responsibility, and (perhaps) policing
whether that responsibility is met. Specifically, it's an architectural thing:
the Achitect has deemed that <this place in the code> shall be responsible for
handling errors (etc) detected in <these places in the code>. (Or you could
even say it's an organisational thing, and replace <place in the code> with
<programmer> -- it doesn't make all that much different from my point of view).
So there are two questions to answer. (1) are notifications of errors (etc)
channeled to the righjt place ? Java has the tools to handle that very nicely.
(2) Are all the errors which are supposed to reach <the right place> actually
handled there ? Ideally we'd like to be able to police this automatically.

But we can't do that without accepting one or another of several rather
unappealing options: we can increase coupling and reduce flexibility by
requiring that every method "carry-forward" the contract of everything it
touches; or we can require whole program analysis; or we can weaken the
checking to the point where it doesn't cover everthing we'd like it to.

(As an aside, you may remember that I'm no big fan of static analysis and
checking -- this is one of the cases where I feel much more positive about it,
specifically /because/ the relationship between the parties is non-local --
unlike parameter types, say.)

-- chris
 
C

Chris Smith

Chris Uppal said:
Contrariwise, there is no reason why bbb() should expose its relationship with
inner() -- that is also breaking abstraction.

If bbb were exposing its relationship with inner, then that would be
breaking abstraction. That's not what's going on, though. bbb is
making the (true) statement that it can fail in certain ways. Whether
it can fail that way because it calls inner or for some other reason, is
immaterial and is properly hidden behind an abstraction boundary.

It seems to me you're proposing we break that boundary. What would
strike me as odd is if bbb needed a different publicly visible
declaration depending on whether it throws IOException by using a
FileInputStream directly, or by calling inner to do the same thing.
That is not a choice that affects anyone except bbb.

Another way of putting the same thing: if it's too hard to construct and
throw some complex exception directly, I ought to be able to say

private void throwException(ComplexData d) throws ComplexException
{
...
}

then I could reasonably expect that the line:

public void foo()
{
...
throwException(myComplexData);
...
}

would be approximately equivalent to a throw statement. Specifically,
it should fail validation. But it seems that you're saying that it
should be fine, because it's just acting as a carrier for that exception
and shouldn't need to know about it. Yet that's not really true. In
every way, my foo absolutely knows that it may throw that exception, and
it should know. I just made the irrelevant implementation choice to
encapsulate some complex block of code into a separate method. I don't
want my static validation to break as a result.
In fact it is complicating the relationship between aaa() and bbb() by
introducing irrelevant (to them) complications and conditions on how
they fit together.

How is it irrelevant? It's only irrelevant if there's some other way
for outer to know that it's indirectly calling inner or otherwise
incurring those error conditions. But ANY other way for that knowledge
to exist besides via aaa is necessarily violating the abstraction
boundaries defined by aaa. That is necessary because I can make that
piece of knowledge false simply by changing the implementation of aaa;
and changing the implementation of aaa is right at the core of what
abstraction is supposed to protect me from.
I think we are still differing in terminology -- this time about "contract".
For me it is about assigning a certain responsibility, and (perhaps) policing
whether that responsibility is met. Specifically, it's an architectural thing:
the Achitect has deemed that <this place in the code> shall be responsible for
handling errors (etc) detected in <these places in the code>.

What if the "Architect" has forgotten to do so?
But we can't do that without accepting one or another of several rather
unappealing options: we can increase coupling and reduce flexibility by
requiring that every method "carry-forward" the contract of everything it
touches;

This isn't nearly so unreasonable as you make it sound. The fact is
that aaa CAN fail in those ways. Your choices are to declare it, or (in
some other language) to not declare it. The only arguments I see for
not declaring it are:

1. The Java language features are not sufficient to capture everything
you want here; e.g., the callback example elsewhere.

2. It's too much typing.

I don't see anything else that's valid. How could this possibly
increase coupling? The coupling is there whether you choose to ignore
it or not. Flexibility? The obligation exists to handle that error
whenever you call aaa, whether or not you declare it. These attributes
are all common to the validated and non-validated situations; it's just
that in one case, someone holds it all in their head.
 
J

James Harris

I see what you mean.

If I understand you correctly, the complaint with Java is the notational
overhead of actually having to put the words "throws Blah" in the method
signature.

I think that the "self-documentation" feature of those words would offset the
overhead for many programmers, myself included, but I agree that those words
are overhead. You are most likely correct that some Java-like language could
have checked exceptions in the way that you describe.

There is a purpose for that overhead in Java. The question boils down to
whether the purpose is sufficient to justify the overhead. You think it is
not. I respect that.

I haven't caught up with all the posts yet so this may have been
discussed but to come back to my original point - and my original and
only complaint - about Java's syntax: if I have a /chain/ of methods,
want to detect errors in the lowest and handle them in the highest
doesn't this mean there is a greater 'overhead' in method signatures
than your post suggests? Doesn't each and every intervening method
need the same throws clause?

If the lower methods declare they will throw just five checked
exceptions don't these all need declaring in every intervening method?
If there are ten intervening methods isn't that 50 exceptions to add?
The situation is, in fact, worse than this suggests as each method may
well call more than one other method, each of which throws its own
errors.

In other words my challenge on Java is that checked exceptions don't
seem to scale well. I understand this issue can be "fixed" by various
means but these are not natural parts of the language.
 
J

James Harris

I think you are missing the OP's point. His interest (as I understand it)
isn't in how to use Java effectively, but in how to design programming
languages. Which is why he asked for our experiences as users of one
experiment in design -- namely checked exceptions in Java.

[OP] Yes, that was my reason for raising this. Perhaps we should start
a thread in only c.l.m. Don't want to upset the Java folks too
much :) In fact, I am trying to learn Java too and am intrigued, to
say the least, by how much people like this feature.
 
R

Robbert Haarman

[OP] Yes, that was my reason for raising this. Perhaps we should start
a thread in only c.l.m. Don't want to upset the Java folks too
much :) In fact, I am trying to learn Java too and am intrigued, to
say the least, by how much people like this feature.

It's funny you should say that, because that has been my experience with
everything I don't like about Java.

FYI, Java was one of the first programming languages I learned (after
BASIC, x86 assembly and C), and, initially, I drank the Kool-Aid and
thought it was the greatest language in the world. Then, when I started
to learn other languages (PHP, Scheme, Common Lisp, Ruby, OCaml, among
others, in some order), I discovered that Java is fairly verbose and
rigid; there is One Right Way, and many things that can be expressed
elegantly in other languages look clumsy in Java.

There are certainly good arguments for the way things are done in Java
(for example, the One Right Way makes it much more likely that different
Java programmers can easily understand each other's code), and I think
Java is a passable programming language. Still, Java code often makes me
shudder. I am sure Java programmers shudder when they see some of my
code, too.

Regards,

Bob
 
L

Lew

James said:
I haven't caught up with all the posts yet so this may have been
discussed but to come back to my original point - and my original and
only complaint - about Java's syntax: if I have a /chain/ of methods,
want to detect errors in the lowest and handle them in the highest
doesn't this mean there is a greater 'overhead' in method signatures
than your post suggests? Doesn't each and every intervening method
need the same throws clause?

If the lower methods declare they will throw just five checked
exceptions don't these all need declaring in every intervening method?
If there are ten intervening methods isn't that 50 exceptions to add?
The situation is, in fact, worse than this suggests as each method may
well call more than one other method, each of which throws its own
errors.

In other words my challenge on Java is that checked exceptions don't
seem to scale well. I understand this issue can be "fixed" by various
means but these are not natural parts of the language.

That would not be the usual handling of exceptions, but anyway it isn't 50
exceptions, it's five exceptions on ten methods.

This is similar to the overhead of declaring the parameters for each of the
ten methods.

If you wrote a chain of methods that rethrow checked exceptions all the way
up, that was a design decision and presumably there was a good reason for it.
Other idioms exists, however, so this choice is not mandatory. The "fixes"
are, indeed, a "natural part" of Java, since the natural idioms in Java are:

- Catch and handle the exception - no rethrow.
- Catch and rethrow, or simply pass through a checked exception - the scenario
you did not like but is really just fine.
- Catch a checked exception and wrap in an unchecked one, then throw the
unchecked exception - no throws clause needed above this.
- Catch the five exceptions at the lowest level, log them, then wrap in a
single, application-specific checked exception that all higher methods recognize.

This last is probably the most common. Once you've handled the exception at
the lowest level, you really don't care any more whether it was a
ServletException or an IOException; at that point it is just a MyAppException
with the original Throwable as its cause.

All these are quite "natural" and most of them reduce the overhead of the
throws clause.

Personally, I think all this fuss over a little extra typing for the throws
clause is a combination of laziness and not understanding the exception idioms.

- Lew
 
J

James Harris

[OP] Yes, that was my reason for raising this. Perhaps we should start
a thread in only c.l.m. Don't want to upset the Java folks too
much :) In fact, I am trying to learn Java too and am intrigued, to
say the least, by how much people like this feature.

It's funny you should say that, because that has been my experience with
everything I don't like about Java.

FYI, Java was one of the first programming languages I learned (after
BASIC, x86 assembly and C), and, initially, I drank the Kool-Aid and
thought it was the greatest language in the world. Then, when I started
to learn other languages (PHP, Scheme, Common Lisp, Ruby, OCaml, among
others, in some order), I discovered that Java is fairly verbose and
rigid; there is One Right Way, and many things that can be expressed
elegantly in other languages look clumsy in Java.

There are certainly good arguments for the way things are done in Java
(for example, the One Right Way makes it much more likely that different
Java programmers can easily understand each other's code), and I think
Java is a passable programming language. Still, Java code often makes me
shudder. I am sure Java programmers shudder when they see some of my
code, too.

Just out of curiosity, have you looked at and what do you think of
Python and Jython? I got a book on Jython this week and it looks a
much cleaner way to target the JVM. Before anyone complains, I'm not
saying it is "better" - it is clearly not in some cases - but maybe
its syntax would appeal more. To pick an example from the book:

Java: MyClass instance = (MyClass)list.get(2);
Jython: instance = list[2]
 
L

Lew

James Harris wrote:

Characterizing the style of the language as "The One Right Way" is cute, but
not apt. Many things I call "elegant" in Java you call "ugly". Many things
you call "elegant" in another language I call "loosey-goosey and unsafe".

À chacun son goût.

Like every language, Java was designed to support certain things at the
expense of others. You think Java is "verbose" and "rigid", which is your
right. Others might find those areas "self-documenting" and "sturdy".

Rigid is good when it's a guard rail at the edge of a precipice.

- Lew
 
A

Arthur J. O'Dwyer

[Xpost to c.l.j.help removed.]

Actually, that would be

MyClass instance = list.get(2);

Assuming a correct declaration of list.

If your list stores Objects, you need the downcast. (Unless this
aspect of Java has changed since four years ago, which is certainly
possible.) And a sufficiently general-purpose ADT must store Objects
(unless you're using a later Java, with generics or templates or
parameterized types or whatever it has now).
Jython: instance = list[2]

Where is the compile-time safety?

It didn't go anywhere. "list[2]" contains exactly as many type
signifiers as "list.get(2)" --- in fact, it contains one more, in
that "[2]" signifies an array access, whereas any old object can
implement a "get" method.

-Arthur
 
R

Robbert Haarman

[Xpost to c.l.j.help removed.]

Actually, that would be

MyClass instance = list.get(2);

Assuming a correct declaration of list.

If your list stores Objects, you need the downcast. (Unless this
aspect of Java has changed since four years ago, which is certainly
possible.) And a sufficiently general-purpose ADT must store Objects
(unless you're using a later Java, with generics or templates or
parameterized types or whatever it has now).

Java has had generics since 1.5 (or 5.0, or however its versioning
works). Basically, the type system is more ML-like now, but without type
inference.
Jython: instance = list[2]

Where is the compile-time safety?

Not there in Python, but I would like to note that in OCaml (and other
ML dialects), the above could be fully type-safe and statically
chcekable.

Regards,

Bob

--
Dude, where's my cdr?


-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)

iD8DBQFF3N2ifb9wcmD+WN4RAosKAJ0S6v6nGeqy6nRPMxrCQsYzRk10VQCgtwEB
nskdcwASo3k0lcsm7h/fyd0=
=xJvw
-----END PGP SIGNATURE-----
 
?

=?ISO-8859-1?Q?Arne_Vajh=F8j?=

Arthur said:
If your list stores Objects, you need the downcast. (Unless this
aspect of Java has changed since four years ago, which is certainly
possible.) And a sufficiently general-purpose ADT must store Objects
(unless you're using a later Java, with generics or templates or
parameterized types or whatever it has now).

It changed a couple of years ago with generics.

And that is the correct way of doing it today.

Arne
 
R

Robbert Haarman

Characterizing the style of the language as "The One Right Way" is cute,

See below.
but not apt. Many things I call "elegant" in Java you call "ugly". Many
things you call "elegant" in another language I call "loosey-goosey and
unsafe".

À chacun son goût.

True. By the way, are people who don't speak French supposed to
understand that last sentence?
Like every language, Java was designed to support certain things at the
expense of others.

That's where you are wrong. Java was designed to support certain things
and not others, and that's what makes it a One True Way language. Other
languages (albeit few) are designed to be multi-paradigm and let the
programmer choose which way to do things.
You think Java is "verbose" and "rigid", which is your
right. Others might find those areas "self-documenting" and "sturdy".

Absolutely right.

Regards,

Bob
 
G

Guest

Robbert said:
What functionality?

That it can throw an exception.
Interfaces probably should be an exception to the rule in that the
programmer must specify the exception list. As you point out correctly,
there is no code the list could be inferred from.

And the same can be the case for abstract classes.

I like the consistency in the choice Java made.

Arne
 
G

Guest

Robbert said:
That's where you are wrong. Java was designed to support certain things
and not others, and that's what makes it a One True Way language. Other
languages (albeit few) are designed to be multi-paradigm and let the
programmer choose which way to do things.

That is correct for most things.

But somewhat funny - not for exceptions, where Java actually did choose
multi paradigm.

Arne
 
G

Guest

Lew said:
James said:
Jython: instance = list[2]

Where is the compile-time safety?

Python has a different approach to types than
C/C++, Java, C# etc..

You can call that not type safe. But that concept
does not have the same negative meaning as in the
above mentioned languages.

Arne
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,774
Messages
2,569,598
Members
45,144
Latest member
KetoBaseReviews
Top