Exception Declaration and the Public Interface
- Details
- Category: Programming Guides & Tutorials
- Published on Monday, 21 January 2013 11:54
- Written by Vinayaga Moorthy
Exception Declaration and the Public Interface
So, how do we know that some method throws an exception that we have to catch? Just as a method must specify what type and how many arguments it accepts and what is returned, the exceptions that a method can throw must be declared (unless the exceptions are subclasses of RuntimeException). The list of thrown exceptions is part of a method's public interface. The throws keyword is used as follows to list the exceptions that a method can throw:
void myFunction() throws MyException1, MyException2 {
// code for the method here
}
This method has a void return type, accepts no arguments, and declares that it can throw one of two types of exceptions: either type MyException1 or type MyException2. (Just because the method declares that it throws an exception doesn't mean it always will. It just tells the world that it might.)
Suppose your method doesn't directly throw an exception, but calls a method that does. You can choose not to handle the exception yourself and instead just declare it, as though it were your method that actually throws the exception. If you do declare the exception that your method might get from another method, and you don't provide a try/catch for it, then the method will propagate back to the method that called your method, and either be caught there or continue on to be handled by a method further down the stack.
Any method that might throw an exception (unless it's a subclass of RuntimeException) must declare the exception. That includes methods that aren't actually throwing it directly, but are "ducking" and letting the exception pass down to the next method in the stack. If you "duck" an exception, it is just as if you were the one actually throwing the exception. RuntimeException subclasses are exempt, so the compiler won't check to see if you've declared them. But all non- RuntimeExceptions are considered "checked" exceptions, because the compiler checks to be certain you've acknowledged that "bad things could happen here."
Remember this:
Each method must either handle all checked exceptions by supplying a catch clause or list each unhandled checked exception as a thrown exception.
This rule is referred to as Java's "handle or declare" requirement. (Sometimes called "catch or declare.")
Look for code that invokes a method declaring an exception, where the calling method doesn’t handle or declare the checked exception. The following code (which uses the throw keyword to throw an exception manually - more on this next) has two big problems that the compiler will prevent:
void doStuff() {
doMore();
}
void doMore() {
throw new IOException();
}
First, the doMore() method throws a checked exception, but does not declare it! But suppose we fix the doMore() method as follows:
void doMore() throws IOException { … }
The doStuff() method is still in trouble because it, too, must declare the IOException, unless it handles it by providing a try/catch, with a catch clause that can take an IOException.
Again, some exceptions are exempt from this rule. An object of type RuntimeException may be thrown from any method without being specified as part of the method's public interface (and a handler need not be present). And even if a method does declare a RuntimeException, the calling method is under no obligation to handle or declare it. RuntimeException, Error, and all of their subtypes are unchecked exceptions and unchecked exceptions do not have to be specified or handled. Here is an example:
import java.io.*;
class Test {
public int myMethod1() throws EOFException {
return myMethod2();
}
public int myMethod2() throws EOFException {
// code that actually could throw the exception goes here
return 1;
}
}
Let's look at myMethod1(). Because EOFException subclasses IOException and IOException subclasses Exception, it is a checked exception and must be declared as an exception that may be thrown by this method. But where will the exception actually come from? The public interface for method myMethod2() called here declares that an exception of this type can be thrown. Whether that method actually throws the exception itself or calls another method that throws it is unimportant to us; we simply know that we have to either catch the exception or declare that we throw it. The method myMethod1() does not catch the exception, so it declares that it throws it. Now let's look at another legal example, myMethod3().
public void myMethod3() {
// code that could throw a NullPointerException goes here
}
According to the comment, this method can throw a NullPointerException. Because RuntimeException is the superclass of NullPointerException, it is an unchecked exception and need not be declared. We can see that myMethod3() does not declare any exceptions.
Runtime exceptions are referred to as unchecked exceptions. All other exceptions are checked exceptions, and they don't derive from java.lang.RuntimeException. A checked exception must be caught somewhere in your code. If you invoke a method that throws a checked exception but you don't catch the checked exception somewhere, your code will not compile. That's why they're called checked exceptions; the compiler checks to make sure that they're handled or declared. A number of the methods in the Java 2 Standard Edition libraries throw checked exceptions, so you will often write exception handlers to cope with exceptions generated by methods you didn't write.
You can also throw an exception yourself, and that exception can be either an existing exception from the Java API or one of your own. To create your own exception, you simply subclass Exception (or one of its subclasses) as follows:
class MyException extends Exception { }
And if you throw the exception, the compiler will guarantee that you declare it as follows:
class TestEx {
void doStuff() {
throw new MyException(); // Throw a checked exception
}
}
The preceding code upsets the compiler:
TestEx.java:6: unreported exception MyException; must be caught
or
declared to be thrown throw new MyException();
When an object of a subtype of Exception is thrown, it must be handled or declared. These objects are called checked exceptions, and include all exceptions except those that are subtypes of RuntimeException, which are unchecked exceptions. Be ready to spot methods that don’t follow the “handle or declare” rule, such as
class MyException extends Exception {
void someMethod () {
doStuff();
}
void doStuff() throws MyException {
try {
throw new MyException();
}
catch(MyException me) {
throw me;
}
}
}
You need to recognize that this code won’t compile. If you try, you’ll get
MyException.java:3: unreported exception MyException; must be caught or declared to be thrown
doStuff();
^
Notice that someMethod() fails to either handle or declare the exception that can be thrown by doStuff().
You need to know how an Error compares with checked and unchecked exceptions. Objects of type Error are not Exception objects, although they do represent exceptional conditions. Both Exception and Error share a common superclass, Throwable, thus both can be thrown using the throw keyword. When an Error or a subclass of Error is thrown, it's unchecked. You are not required to catch Error objects or Error subtypes. You can also throw an Error yourself (although other than AssertionError you probably won't ever want to), and you can catch one, but again, you probably won't. What, for example, would you actually do if you got an OutOfMemoryError? It's not like you can tell the garbage collector to run; you can bet the JVM fought desperately to save itself (and reclaimed all the memory it could) by the time you got the error. In other words, don't expect the JVM at that point to say, "Run the garbage collector? Oh, thanks so much for telling me. That just never occurred to me. Sure, I'll get right on it." Even better, what would you do if a VirtualMachineError arose? Your program is toast by the time you'd catch the Error, so there's really no point in trying to catch one of these babies. Just remember, though, that you can! The following compiles just fine:
class TestEx {
public static void main (String [] args) {
badMethod();
}
static void badMethod() { // No need to declare an Error
doStuff()
}
static void doStuff() { //No need to declare an Error
try {
throw new Error();
}
catch(Error me) {
throw me; // We catch it, but then rethrow it
}
}
}
If we were throwing a checked exception rather than Error, then the doStuff() method would need to declare the exception. But remember, since Error is not a subtype of Exception, it doesn't need to be declared. You're free to declare it if you like, but the compiler just doesn't care one way or another when or how the Error is thrown, or by whom.
Because Java has checked exceptions, it's commonly said that Java forces developers to handle exceptions. Yes, Java forces us to write exception handlers for each exception that can occur during normal operation, but it's up to us to make the exception handlers actually do something useful. We know software managers who melt down when they see a programmer write:
try {
callBadMethod();
} catch (Exception ex) { }
Notice anything missing? Don't "eat" the exception by catching it without actually handling it. You won't even be able to tell that the exception occurred, because you'll never see the stack trace.
Rethrowing the Same Exception
Just as you can throw a new exception from a catch clause, you can also throw the same exception you just caught. Here's a catch clause that does this:
catch(IOException e) {
// Do things, then if you decide you can't handle it…
throw e;
}
All other catch clauses associated with the same try are ignored, if a finally block exists, it runs, and the exception is thrown back to the calling method (the next method down the call stack). If you throw a checked exception from a catch clause, you must also declare that exception! In other words, you must handle and declare, as opposed to handle or declare. The following example is illegal:
public void doStuff() {
try {
// risky IO things
} catch(IOException ex) {
// can't handle it
throw ex; // Can't throw it unless you declare it
}
}
In the preceding code, the doStuff() method is clearly able to throw a checked exception—in this case an IOException—so the compiler says, "Well, that's just peachy that you have a try/catch in there, but it's not good enough. If you might rethrow the IOException you catch, then you must declare it!"
blog comments powered by Disqus
More 

