Concepts / Throwing Exceptions for Precondition Violations

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.

  • Programming
Interactive lab

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

  1. Statements in try run until one raises an exception; the rest of try is skipped.
  2. Python checks the except clauses in order and runs the first one whose type matches.
  3. If nothing was raised, the else block runs instead.
  4. 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.

Educational simulation

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?

  • Yes, the exception is thrown on every call

  • No, the clause only announces what the routine might throw

  • Yes, but only when the inputs are bad

Reveal answer
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.

java
Notes on the code
  • 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
public staticmodifiersdoublereturn typeroot(double a, doubleb, double c)name and parameter listthrowskeyword that starts theclauseIllegalArgumentExceptiondeclares a possibleexception, throws nothing
Which parts of the header are the modifiers, return type, name and parameters, and throws clause? The throws clause names an exception class but does not throw anything.

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

Definition

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.

Rule

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.

A is zero ordiscriminantnegative?throwIllegalArgumentExceptionfailure pathcompute the rootsnormal pathreturn the resultyesno
When the precondition check fails, which path does execution take compared with the normal path? The normal work step never runs on the failure path.

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.

maincalls root with bad inputsrootthrow runs hereprogram crashesno compatible catchcallexception propagates outwardno catch
After root throws, which frame is skipped, and where does execution end up if main has no matching catch?

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.

Code walkthrough
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);
}
  1. 1
    Line 2 tests the first precondition: is A zero?
  2. 2
    Line 3 throws when A is zero, so control leaves root here and the lines below never run.
  3. 3
    Line 5 computes the discriminant only after A has passed its check.
  4. 4
    Line 6 tests the second precondition: is the discriminant negative?
  5. 5
    Line 7 throws when the discriminant is negative, so the square root is never taken.
  6. 6
    Line 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.

java
Notes on the code
  • Two exception class names follow the throws keyword, separated by a comma
  • The parameter list is unchanged by the clause
QuestionHeader with no throws clauseHeader with throws ExceptionTypeOne, ExceptionTypeTwo
What the reader learns from the headerNothing about possible exceptionsThe routine might throw either named type
Does the header itself throw anything?NoNo, the clause only declares possibilities
What raises an exception at runtimeA throw statement in the bodyA throw statement in the body, and only when a precondition fails
Does naming both types mean both always occur?Not applicableNo, 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 like

    Calling root(2, 4, 1) with a valid A and a non-negative discriminant, and expecting an IllegalArgumentException because the header names one.

    Why it fails

    The 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 instead

    Look 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 like

    Believing that because root names IllegalArgumentException, passing A equal to zero is handled safely without any exception.

    Why it fails

    The declaration does not guard the routine. Bad inputs still reach the throw statement inside the body.

    Do this instead

    Keep 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 like

    Assuming root still computes the roots after finding a negative discriminant and then reports the error.

    Why it fails

    Control leaves the subroutine when the exception is thrown, so the remaining statements do not run.

    Do this instead

    Trace 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 like

    Writing a caller with no try..catch and expecting the program to continue past the failed call.

    Why it fails

    If no caller has a compatible handler, the exception propagates outward and the program stops.

    Do this instead

    Either 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

MEDIUM

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.