Logo

dev-resources.site

for different kinds of informations.

Effective Java: Throw Exceptions Appropriate To The Abstraction

Published at
9/9/2021
Categories
java
effective
exceptions
architecture
Author
kylec32
Author
7 person written this
kylec32
open
Effective Java: Throw Exceptions Appropriate To The Abstraction

Much of Effective Java focuses on building a clean, understandable API and how that is the foundation of a great library. Part of the API of a class is any exceptions it may throw up the stack both checked (where it becomes part of the signature) or unchecked. As writers of the code, it is our responsibility to make sure that there aren't any surprises or anything shocking from this API. One of the ways this can happen is with the exposure of an exception that doesn't make sense for the class we are writing.

A potential example of a mismatched exception would be if you requested two numbers to be added together and the method threw an IOException. I would have many questions if such a method threw an IOException but the issue still stands that the exception thrown does not match the abstraction. By throwing this low-level exception you are exposing implementation details to the caller, implementation details that may change in the future but now it is part of your API so it makes it hard to change. So what are some ways to account for this?

The primary method employed to account for this issue is to do what is called exception translation. Exception translation is when you catch a lower-level exception and wrap it in a higher-level exception that matches the abstraction you are dealing with. We can find an example of this in the AbstractSequentialList class.

public E get(int index) {
  ListIterator<E> = listIterator(index);
  try {
    return i.next();
  } catch (NoSuchElementException e) {
    throw new IndexOutOfBoundsException("Index: " + index);
  }
}
Enter fullscreen mode Exit fullscreen mode

In this case, the interface it is implementing even tells it that IndexOutOfBoundsException is the exception that should be thrown from this method.

A special form of exception translation is when you wrap the lower-level exception in a higher-level exception but also pass the lower-level exception into the higher-level exception as a cause. Many methods expose this cause field and it gets passed up to the Throwable class. If a particular method doesn't expose a cause in its construction you can even call Throwable's initCause on the exception. Throwable provides a getCause method that can then be used higher in the stack to retrieve the underlying issue. Even more importantly, this cause is exposed via stack traces which can greatly help the debugging of issues. This does expose lower-level details to a calling method indirectly. It, however, doesn't do it very directly thus it doesn't force the caller to handle the low-level exception, but instead, they can still handle the high-level exception and not worry about the low-level implementation details.

The easiest exception to handle is the exception that doesn't get thrown. We should always strive in all of our code to not throw avoidable exceptions. Effective Java even suggests at times we can work around exceptions and simply log them and keep moving on. I would caution against the use of this pattern though. If we are only throwing an exception in exceptional cases then likely the caller needs to know that something happened and simply hiding it from the caller can be problematic.

Always remember that any thrown exceptions are part of your API. As such, you should take into consideration what exception you are throwing and bubbling up to your callers. Make sure those exceptions are appropriate to the abstraction your code is working at. If you do have lower-level exceptions being thrown use the exception translation pattern to translate that exception into a higher-level exception type appropriate to your abstraction. Also, consider making use of the cause field in such caught and rethrown exceptions. Through doing this you can end up with easier to read, maintain, and modify code.

effective Article's
30 articles in total
Favicon
What is The Best Time to Learn New Things Effectively?
Favicon
Designing an Efficient Biomass Generator Set System
Favicon
Top 10 Essential Features for an Effective Knowledge Base
Favicon
Effective Java: Consider Serialization Proxies Instead of Serialized Instances
Favicon
Effective Java: For Instance Control, Prefer Enum types to readResolve
Favicon
Effective Java: Write readObject Methods Defensively
Favicon
Effective Java: Consider Using a Custom Serialized Form
Favicon
Effective Java: Implement Serializable With Great Caution
Favicon
Effective Java: Prefer Alternatives To Java Serialization
Favicon
Effective Java: Don't Depend on the Thread Scheduler
Favicon
Effective Java: Use Lazy Initialization Judiciously
Favicon
Effective Java: Document Thread Safety
Favicon
Effective Java: Prefer Concurrency Utilities Over wait and notify
Favicon
Effective Java: Prefer Executors, Tasks, and Streams to Threads
Favicon
Effective Java: Avoid Excessive Synchronization
Favicon
Effective Java: Synchronize Access to Shared Mutable Data
Favicon
Effective Java: Don't Ignore Exceptions
Favicon
Effective Java: Strive for Failure Atomicity
Favicon
Effective Java: Include Failure-Capture Information in Detail Messages
Favicon
Effective Java: Document All Exceptions Thrown By Each Method
Favicon
Effective Java: Throw Exceptions Appropriate To The Abstraction
Favicon
Effective Java: Favor The Use of Standard Exceptions
Favicon
Effective Java: Avoid Unnecessary Use of Checked Exceptions
Favicon
Effective Java: Use Checked Exceptions for Recoverable Conditions
Favicon
Effective Java: Use Exceptions for Only Exceptional Circumstances
Favicon
Effective Java: Adhere to Generally Accepted Naming Conventions
Favicon
Effective Java: Optimize Judiciously
Favicon
Effective Java: Use Native Methods Judiciously
Favicon
Effective Java: Prefer Interfaces To Reflection
Favicon
10 effective tips for New Developers

Featured ones: