Prompt
Design exceptions to provide clear context, preserve stack traces, and propagate correctly through your system. Key practices include:
- Choose the most appropriate exception type based on the error condition:
- Use
IllegalArgumentExceptionfor invalid inputs - Use
IllegalStateExceptionfor invalid state - Avoid generic exceptions when more specific ones exist
- Use
// Poor practice
throw new ValidationException("Cannot unwrap to " + type);
// Better practice
throw new IllegalArgumentException("Cannot unwrap " + target + " to " + type.getName());
- Avoid unnecessary exception wrapping that obscures the original cause. Let original exceptions propagate when they provide sufficient context:
// Unnecessary wrapping
try {
this.jobLauncher.launch(job, jobParameters);
} catch (NoSuchJobException ex) {
throw new JobExecutionException(ex.getMessage(), ex);
}
// Better approach - NoSuchJobException is already a JobExecutionException
// Remove the try/catch completely
this.jobLauncher.launch(job, jobParameters);
- For cleanup operations during error handling, capture secondary failures as suppressed exceptions:
catch (RuntimeException ex) {
try {
WebServer webServer = getWebServer();
if (webServer != null) {
webServer.stop();
}
}
catch (Exception shutdownEx) {
ex.addSuppressed(shutdownEx);
}
throw ex;
}
- Prefer failing fast with specific exceptions when unexpected conditions occur rather than continuing with defensive fallbacks that might mask issues:
// Defensive (might mask issues)
Method initializeMethod = ReflectionUtils.findMethod(this.tester, "initialize", Class.class, ResolvableType.class);
if (initializeMethod == null) {
throw new IllegalStateException("unable to find initialize method for " + this.tester);
}
// Fail-fast (better for critical components)
Method initializeMethod = ReflectionUtils.findMethod(this.tester, "initialize", Class.class, ResolvableType.class);
// Let the NPE happen naturally if the method isn't found
- Ensure proper error propagation in asynchronous contexts where traditional exception handling might not work:
// Errors suppressed:
webClient.post()
.bodyValue(body)
.retrieve()
.toBodilessEntity()
.subscribe((__) -> {}, (__) -> {});
// Proper error handling:
webClient.post()
.bodyValue(body)
.retrieve()
.toBodilessEntity()
.subscribe((__) -> {}, (error) -> errorHandler.handleError(error));