How to handle checked exceptions on interface contract in Java

I am starting a project in Java and ran into the following situation. The application requires a persistence layer for a certain document type. This could be a MySql database, an AWS Dynamo DB instance, a wrapped REST API, etc. The bare bones interface not considering error handling is: public interface PersistenceLayer { boolean Put(Document doc); Optional Get(DocumentKey key); } But consider that for error handling both Put and Get can fail with different exceptions. The SQL implementation would have its own issues, the AWS one would have a different set, etc. It seems like there are the following options for how to properly surface this on the interface: Add throws Exception on both of the methods. The actual implementations would specify the actual exceptions that are thrown. Those who code to the interface would not be able to properly distinguish different errors, but most likely the logic would just be to log and move along in which case this is sufficient. It seems like a code smell though to do this. A slight improvement on the above may be to create a PersistenceLayerOperationException which wraps the implementation exceptions. Although semantically this on its own doesn't allow for better exception handling at the call site, it does allow for some future improvements. For example, when wrapping the underlying exception, information could be added such as if the failure is transient, or specific log information beyond what is provided by Exception. Simply use unchecked exceptions and do not include any checked exceptions on the API, since there's no real action that the user can take on them. This seems like a cop out to me since there are exceptions that can easily happen and the implementation would then have to do the work of catching the checked exceptions that are exposed by sql operations for example, and resurfacing them without the end user actually realizing the failure modes of the operation. I cannot find much guidance online of how to handle this case, where interface operations have some expected failures, but the exception set is not fixed.

Jan 26, 2025 - 17:24
 0
How to handle checked exceptions on interface contract in Java

I am starting a project in Java and ran into the following situation. The application requires a persistence layer for a certain document type. This could be a MySql database, an AWS Dynamo DB instance, a wrapped REST API, etc.

The bare bones interface not considering error handling is:

public interface PersistenceLayer {
  boolean Put(Document doc);
  Optional Get(DocumentKey key);
}

But consider that for error handling both Put and Get can fail with different exceptions. The SQL implementation would have its own issues, the AWS one would have a different set, etc.

It seems like there are the following options for how to properly surface this on the interface:

  1. Add throws Exception on both of the methods. The actual implementations would specify the actual exceptions that are thrown. Those who code to the interface would not be able to properly distinguish different errors, but most likely the logic would just be to log and move along in which case this is sufficient. It seems like a code smell though to do this.
  2. A slight improvement on the above may be to create a PersistenceLayerOperationException which wraps the implementation exceptions. Although semantically this on its own doesn't allow for better exception handling at the call site, it does allow for some future improvements. For example, when wrapping the underlying exception, information could be added such as if the failure is transient, or specific log information beyond what is provided by Exception.
  3. Simply use unchecked exceptions and do not include any checked exceptions on the API, since there's no real action that the user can take on them. This seems like a cop out to me since there are exceptions that can easily happen and the implementation would then have to do the work of catching the checked exceptions that are exposed by sql operations for example, and resurfacing them without the end user actually realizing the failure modes of the operation.

I cannot find much guidance online of how to handle this case, where interface operations have some expected failures, but the exception set is not fixed.