Throwing Exceptions for Precondition Violations
A throws clause in a subroutine header announces the exception classes the routine might throw. It does not throw anything by itself.
Try it: try / except / else / finally
How an exception jumps out of a try block to the first matching except clause, when else runs, and why finally always runs.
How it works
- Statements in try run until one raises an exception; the rest of try is skipped.
- Python checks the except clauses in order and runs the first one whose type matches.
- If nothing was raised, the else block runs instead.
- finally runs in every case.
Default run (9 steps): Call safe_divide('4'). … Return 25.0. Output was: Worked: 25.0 / Done.
Simplified: One fixed function; int() and / are modelled exactly (including how Python prints floats), but nothing is executed as Python.
Loading the simulation…
Predicting What a throws Clause Does
Anyone who calls a subroutine needs to know more than what it returns. They also need to know whether it can fail, and in what way. Java lets a subroutine publish that information in its own header. A header can name the exception classes the subroutine might throw, so a programmer reading only the header learns about a possible failure before reading any of the body. This article follows one example routine through that header, through the code that runs when its inputs are bad, and through the code that calls it.
What do you think happens?
Suppose a routine's header contains a throws clause naming an exception class. Does that clause, by itself, cause the exception to be thrown on every call?
Reveal answer
No, the clause only announces what the routine might throw
A throws clause in a subroutine header announces the exception classes the routine might throw. It does not throw anything by itself. The exception is actually thrown by a throw statement in the body, and that statement runs only when the routine decides to refuse its inputs.
Keep your answer in mind as you read. The clause is a statement about possibility. The rest of this article shows how a possible failure becomes an actual one, and what the caller has to do about it.
Reading the Header of a Routine With a throws Clause
The throws clause sits in the subroutine header, after the parameter list and before the body. Its form is the keyword throws followed by an exception class name. The source uses a method named root that takes three double parameters and returns a double. Its header ends with a clause naming IllegalArgumentException. Modifiers such as static and public come first, then the return type, then the method name and its parameter list. The throws clause is at the far right, outside the parentheses.
- public static: modifiers, listed first
- double: return type, the kind of value the routine gives back
root(double a, double b, double c): method name and parameter list- throws
IllegalArgumentException: the throws clause, outside the parentheses
Read the throws clause as a permission and a warning, not as an action. Nothing inside the parentheses changes when the clause is added. The parameters are still a, b, and c, and the return type is still double. The only new information is that a caller should expect this routine might refuse its inputs with an IllegalArgumentException.
Why a Routine Throws Instead of Continuing
A precondition is a condition that must hold before a subroutine can do its job correctly. Throwing an exception is one way for a routine to respond when a precondition is violated: it refuses to produce a result and signals the refusal by throwing.
The root routine has two preconditions. First, the coefficient A must not be zero. Second, the discriminant B*B - 4*A*C must not be negative. Both are needed because the routine divides by 2*A and takes the square root of the discriminant, and neither operation is meaningful when its precondition fails.
When a precondition is violated, the routine must respond somehow. The alternatives are returning a made-up value or continuing to compute with bad data, and either choice would hide the problem. Throwing an exception is often a reasonable response to an illegal condition found inside a subroutine, because it stops the bad computation at its source and makes the failure impossible to miss.
Following Control After a Throw
Debugging an exception usually starts with one question: where did the actual behavior first differ from what the code was supposed to do? Consider a generated call chain. The main method calls root with values that break a precondition. Inside root, the check fails and the throw statement runs. Control leaves root at that point. Nothing after the throw in root executes, and root does not return a value to main.
The source describes the two possible outcomes for the caller. If the calling code has a try..catch that names a compatible exception type, the caller takes over and runs its handler. If it does not, the exception propagates outward and the program stops. The source describes that stop as the programmer learning that the program needs fixing. In this trace, main has no matching handler, so the program stops.
public static double root(double a, double b, double c) throws IllegalArgumentException {
if (a == 0) {
throw new IllegalArgumentException('A must not be zero');
}
double disc = b*b - 4*a*c;
if (disc < 0) {
throw new IllegalArgumentException('discriminant is negative');
}
return (-b + Math.sqrt(disc)) / (2*a);
}- 1Line 2 tests the first precondition: is A zero?
- 2Line 3 throws when A is zero, so control leaves root here and the lines below never run.
- 3Line 5 computes the discriminant only after A has passed its check.
- 4Line 6 tests the second precondition: is the discriminant negative?
- 5Line 7 throws when the discriminant is negative, so the square root is never taken.
- 6Line 9 is the normal path, reached only when both preconditions hold.
- The throws clause only declares the possibility; the two throw statements are what actually raise the exception
- Each check guards one precondition before the division or square root
Declaring Several Exception Types
A single header can name more than one exception type. The types are separated by commas. The header below is a generated illustration of that form, using placeholder class names rather than real library types. It declares that the routine might throw either of two exceptions.
- Two exception class names follow the throws keyword, separated by a comma
- The parameter list is unchanged by the clause
| Question | Header with no throws clause | Header with throws ExceptionTypeOne, ExceptionTypeTwo |
|---|---|---|
| What the reader learns from the header | Nothing about possible exceptions | The routine might throw either named type |
| Does the header itself throw anything? | No | No, the clause only declares possibilities |
| What raises an exception at runtime | A throw statement in the body | A throw statement in the body, and only when a precondition fails |
| Does naming both types mean both always occur? | Not applicable | No, the declaration does not promise that both are thrown |
What a throws clause tells a reader versus what happens at runtime
Mistakes Readers Make With throws Clauses
Common mistake Assuming the throws clause makes the exception happen on every call
What it looks likeCalling root(2, 4, 1) with a valid A and a non-negative discriminant, and expecting an IllegalArgumentException because the header names one.
Why it failsThe clause only announces what the routine might throw. The exception is raised by a throw statement, which runs only when a precondition fails.
Do this insteadLook in the body for the throw statement and its condition. The header tells you what to expect; the body tells you when it happens.
Common mistake Thinking the throws clause stops the exception when inputs are bad
What it looks likeBelieving that because root names IllegalArgumentException, passing A equal to zero is handled safely without any exception.
Why it failsThe declaration does not guard the routine. Bad inputs still reach the throw statement inside the body.
Do this insteadKeep the two roles separate: the header declares, and the body's check throws.
Common mistake Expecting the normal work step to run after a throw
What it looks likeAssuming root still computes the roots after finding a negative discriminant and then reports the error.
Why it failsControl leaves the subroutine when the exception is thrown, so the remaining statements do not run.
Do this insteadTrace the failure path separately from the normal path, as in the flowchart above.
Common mistake Assuming an uncaught exception is handled automatically
What it looks likeWriting a caller with no try..catch and expecting the program to continue past the failed call.
Why it failsIf no caller has a compatible handler, the exception propagates outward and the program stops.
Do this insteadEither add a try..catch with a compatible exception type in a caller, or accept that the program stops and fix the bad input.
Common mistake Believing that every type in a multi-type throws clause must be thrown on every call
Tracing a Throw Through a Calling Chain
A generated method check(int x) is declared with the header public static void check(int x) throws IllegalArgumentException. Its body throws IllegalArgumentException only when x is negative. A method main calls check(-3) inside no try block. Trace what happens: which statements run inside check, whether main continues after the call, and where the program ends up. Then explain what would change if main wrapped the call in a try..catch naming IllegalArgumentException.
Hints
- Start with the throws clause and ask whether it causes the throw by itself.
- Find the condition that leads to the throw statement inside check.
- After control leaves check, look for a matching handler in main before deciding where execution goes.
Key Takeaways
- A throws clause in a subroutine header announces the exception classes the routine might throw. It does not throw anything by itself; a throw statement in the body does the throwing.
- A routine with preconditions, such as a non-zero coefficient or a non-negative discriminant, may throw an exception when they are violated instead of returning a made-up value or continuing with bad data.
- When an exception is thrown, control leaves the subroutine. A caller with a compatible try..catch takes over; if no caller catches it, the exception propagates outward and the program stops.
- Several exception types can be declared in one throws clause separated by commas. The declaration lists what the routine might throw and does not guarantee that every listed type will occur.