Custom Exceptions and Handling InterruptedException


Custom Exceptions

Under most circumstances, it is simpler from a code-design standpoint to use existing generic Exception classes when throwing exceptions. This is especially true if you only need the exception to carry a simple error message. In that case, RuntimeException is usually preferred, since it is not a checked Exception. Other exception classes exist for common classes of errors:

  • UnsupportedOperationException - a certain operation is not supported
  • IllegalArgumentException - an invalid parameter value was passed to a method
  • IllegalStateException - your API has internally reached a condition that should never happen, or which occurs
  • as a result of using your API in an invalid way

Cases where you do want to use a custom exception class include the following:

  • You are writing an API or library for use by others, and you want to allow users of your API to be able to specifically catch and handle exceptions from your API, and be able to differentiate those exceptions from other, more generic exceptions.
  • You are throwing exceptions for a specific kind of error in one part of your program, which you want to catch and handle in another part of your program, and you want to be able to differentiate these errors from other, more generic errors.

You can create your own custom exceptions by extending RuntimeException for an unchecked exception, or checked exception by extending any Exception which is not also subclass of RuntimeException, because:

  • Subclasses of Exception that are not also subclasses of RuntimeException are checked exceptions
public class StringTooLongException extends RuntimeException {
	// Exceptions can have methods and fields like other classes
	// those can be useful to communicate information to pieces of code catching
	// such an exception
	public final String value;
	public final int maximumLength;
 
	public StringTooLongException(String value, int maximumLength){
		super(String.format("String exceeds maximum Length of %s: %s", maximumLength, value));
		this.value = value;
		this.maximumLength = maximumLength;
	}
}

Those can be used just as predefined exceptions:

void validateString(String value){
	if (value.length() > 30){
		throw new StringTooLongException(value, 30);
	}
}

And the fields can be used where the exception is caught and handled:

void anotherMethod(String value){
	try {
		validateString(value);
	} catch(StringTooLongException e){
		System.out.println("The string '" + e.value + "' was longer than the max of " 
				+ e.maximumLength );
	}
}

Handling InterruptedException

InterruptedException is a confusing beast - it shows up in seemingly innocuous methods like Thread.sleep(), but handling it incorrectly leads to hard-to-manage code that behaves poorly in concurrent environments.

At its most basic, if an InterruptedException is caught it means someone, somewhere, called Thread.interrupt() on the thread your code is currently running in. You might be inclined to say "It's my code! I'll never interrupt it!" and therefore do something like this:

// Bad. Don't do this.
try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
    // disregard
}

But this is exactly the wrong way to handle an "impossible" event occurring. If you know your application will never encounter an InterruptedException you should treat such an event as a serious violation of your program's assumptions and exit as quickly as possible.

The proper way to handle an "impossible" interrupt is like so:

// When nothing will interrupt your code
try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
    Thread.currentThread().interrupt();
    throw new AssertionError(e);
}

This does two things; it first restores the interrupt status of the thread (as if the InterruptedException had not been thrown in the first place), and then it throws an AssertionError indicating the basic invariants of your application have been violated. If you know for certain that you'll never interrupt the thread this code runs in this is safe since the catch block should never be reached.

Using Guava's Uninterruptibles class helps simplify this pattern; calling Uninterruptibles.sleepUninterruptibly() disregards the interrupted state of a thread until the sleep duration has expired (at which point it's restored for later calls to inspect and throw their own InterruptedException). If you know you'll never interrupt such code this safely avoids needing to wrap your sleep calls in a try-catch block.

More often, however, you cannot guarantee that your thread will never be interrupted. In particular if you're writing code that will be executed by an Executor or some other thread-management it's critical that your code responds promptly to interrupts, otherwise your application will stall or even deadlock.

In such cases the best thing to do is generally to allow the InterruptedException to propagate up the call stack, adding a throws InterruptedException to each method in turn. This may seem kludgy but it's actually a desirable property - your method's signatures now indicates to callers that it will respond promptly to interrupts.

// Let the caller determine how to handle the interrupt if you're unsure
public void myLongRunningMethod() throws InterruptedException {
     ...
}

In limited cases (e.g. while overriding a method that doesn't throw any checked exceptions) you can reset the interrupted status without raising an exception, expecting whatever code is executed next to handle the interrupt. This delays handling the interruption but doesn't suppress it entirely.

// Suppresses the exception but resets the interrupted state letting later code
// detect the interrupt and handle it properly.
try {
      Thread.sleep(1000);
} catch (InterruptedException e) {
      Thread.currentThread().interrupt();
      return ...; // your expectations are still broken at this point - try not to do more work.
}

Basic Programs