views:

141

answers:

2
+1  Q: 

Java: Exceptions

Hi,

Why is this code correct:

try
{

} catch(ArrayOutOfBoundsException e)
{

}

and this wrong:

try
{

} catch(IOException e)
{

}

This code is wrong because in the try-body never an IOException is thrown, but in the first body there is also never throw a ArrayOutOfBoundsException. And the first code-piece is correct. Why?? Can I make my own exceptions also like IOException (Must be thrown before catching)?

Thanks

+14  A: 

ArrayIndexOutOfBoundsException is a runtime exception (since it is a child of java.lang.RuntimeException), thus in theory it can be thrown anywhere. All runtime exceptions can be thrown by any code, without the containing method needing to declare it in the throws clause; so the compiler makes no attempt to check that such an exception might actually be thrown by any given code block. This would be impossible in any non-trivial case anyway; any code that includes at least one method invocation on a non-final class could throw such an exception (even if none of your current classes do so, a different subclass could be used at runtime that does so).

On the other hand, IOException is a checked exception, and so can only be thrown by methods that explicitly declare it in their throws clause*.

See this Sun article for a bit more philosophy on checked vs unchecked exceptions. Be aware as well it's a bit of a religious war, with people pushing for all exceptions being one or the other on both sides.

EDIT: To clarify, in your first example the compiler could probably verify that the AIOOBE would never actually be thrown. But it doesn't; firstly because it can only do so in such simple cases (like this one) that it wouldn't give any real benefit; secondly because it would arguably be more confusing if you were sometimes allowed to include "impossible" runtime exceptions but sometimes not, e.g.:

// Preparation stuff
private void myNoop() {}
public void publicNoop() {}
public final void finalNoop() {}

// hypothetically illegal (same as your first example)
try
{
    // do nothing
} catch (ArrayIndexOutOfBoundsException e) {}

// hypothetically illegal (myNoop() can't be overridden)
try
{
    myNoop();
} catch (ArrayIndexOutOfBoundsException e) {}

// hypothetically illegal (finalNoop() can't be overridden)
try
{
    finalNoop();
} catch (ArrayIndexOutOfBoundsException e) {}

// legal (publicNoop() could do anything at runtime)
try
{
    publicNoop();
} catch (ArrayIndexOutOfBoundsException e) {}

That to me seems odd that changing the access level or finality of a method (or in fact a class), would suddenly change the legality of catching certain runtime exceptions. Especially when you consider the catch block may be several levels higher on the stack than the method being changed...

Besides, having a catch block that will never be invoked is harmless, really. "Here's how you deal with an AIOOBE should one arise" - and it never happens at runtime. The same can actually happen with checked exceptions, too; for example, Callable.call() is declared to throw Exception, but the specific implementation you're using might never throw any exceptions - so again, you'd have instructions on how to handle an exception that would never be invoked at runtime.

At the end of the day the compiler is just pointing out discrepancies - "are you sure you meant to catch IOException, since this code will never be run?" It's like static typing, in that it alerts you to changes in interface automatically. Runtime exceptions exist, so that you don't have to declare every single method to throw NullPointerException etc.

*Technically this isn't strictly true, there are a few low-level loopholes to this, but broadly speaking it's the case. The exceptions are generally anomalies, artifacts and/or deprecated anyway.

Andrzej Doyle
"the compiler could probably verify that the AIOOBE would never actually be thrown" - in theory yes. In practice that would be a bad thing since it would mean that the same code would compile or give errors depending on how "smart" the compiler was. Besides, this probably falls foul of some JLS rule :-).
Stephen C
@Stephen: that's exactly my point. I don't think it's possible to detect in any useful way, so not worth detecting at all (the inconsistency would be worse than the benefit in the tiny percentage of caught cases).
Andrzej Doyle
+1  A: 

To answer your other question: Any class that extends Exception (or any of most of its subclasses) will be a checked exception, i.e. one that needs to be caught or declared like IOException.

The exception (unintentional pun) is RuntimeException, which is a subclass of Exception that is NOT checked, and any classes you subclass from RuntimeException (or its subclasses) will not require checking.

Carl Smotricz
It's possibly clearer to state it the other way around - any child of `RuntimeException` (and `Error` if you can to include that too) is unchecked. Any other child of `Exception` (actually `Throwable`) is checked.
Andrzej Doyle