Sobes.tech
Middle

Why is throwing RuntimeException considered bad practice?

sobes.tech AI

Answer from AI

Throwing a RuntimeException without a proper reason is considered bad practice for several reasons:

  1. Lack of information: RuntimeException does not require mandatory handling (checked exception). Throwing such an exception means that the caller may not be aware of the potential problem and will not take measures to fix or handle it. This makes the code less reliable and predictable.
  2. Hidden errors: If RuntimeException is thrown under conditions that could be handled at a higher level, it can mask the real cause of the problem. The exception propagates up the call stack and may be caught at a much higher level, where the context of the error is lost, complicating debugging.
  3. Debugging difficulty: Since RuntimeException does not require declaration in the method signature (throws), it can appear anywhere, making it harder to find the source of the problem. Analyzing the stack trace may take more time.
  4. Violation of contract: Throwing RuntimeException in situations that are essentially part of the expected behavior or a possible but manageable error scenario violates the method's expected contract. The method should either return a result or throw a checked exception that clearly indicates a failure.

Instead of unreasonably using RuntimeException, it is better to use:

  • Checked exceptions (subclass of Exception) for situations that can reasonably occur during program execution and can be handled or recovered from (e.g., IOException).
  • Design without exceptions: When an error can be represented as a return value (e.g., Optional) or a status indicator.
  • Specific subclasses of RuntimeException: If the error is truly unrecoverable programming error (e.g., NullPointerException due to dereferencing null), use existing standard exceptions or create specific subclasses of RuntimeException that more accurately describe the problem.

Example:

Bad practice:

// Bad: throwing RuntimeException without explicit reason
public int divideBad(int a, int b) {
    if (b == 0) {
        throw new RuntimeException("Cannot divide by zero"); // Not informative, no handling
    }
    return a / b;
}

Better practice (using checked exception):

// Good: using checked exception for explicit indication of error
public int divideGood(int a, int b) throws ArithmeticException { // Explicitly declare possible exception
    if (b == 0) {
        // Use more specific and checked exception
        throw new ArithmeticException("Cannot divide by zero");
    }
    return a / b;
}

or (using RuntimeException for unrecoverable error):

// Good: using standard RuntimeException for fundamental error
public void processArray(int[] arr) {
    if (arr == null) {
        throw new NullPointerException("Input array cannot be null"); // Standard RuntimeException
    }
    // ... processing logic
}