IMHO, throwing an exception always exactly means "I could not fulfill my contract".
As an API developer, your steps should be:
- Design the contract (with usefulness for your callers in mind).
- Choose a method name that clearly indicates the contract.
- Implement that method.
Designing the contract
Your contract is something around "find me an A for the given key", but that needs clarification, with the following two candidates:
- "Get me an A with the given key." This version has no clause for not finding an A, so this contract can not be fulfilled then, meaning that an exception has to be thrown.
- "Try to get me an A with the given key." This allows for not finding an A, so this outcome should be indicated in the return value, e.g. using an
Optional<A>
.
Contracts have to be designed with the typical callers in mind.
Analogy: Same as in business. You strive to offer products that meet your customers' needs. Knowing your customers helps, but it's not necessary to know every single customer's mindset in detail.
So, it helps to have some understanding of your callers, but it's not your responsibility to figure out the impact of not finding an A in each and every call stack that arrives at your API.
Back to your method's expected callers:
- Do they expect the matching A to exist? Then the exception is preferrable, as it makes it easy for them to concentrate on the happy path.
- Do you expect callers that will deal with the non-existence in their normal control flow? Then the Optional-based contract is better.
- If you expect callers of both types, let them choose by offering both versions in different methods.
Naming the method
A name like getA()
might imply (e.g. to me) that not returning an A is a violation of contract, but there are too many counter-examples out there to rely on that. Better be explicit in your naming, by using names like getAOrThrow()
or TryGetA()
.
Implementing the method
Once the contract is clear, you can start coding. And if you chose to have both versions, you'd typically implement the exception-throwing one as a small wrapper around the Optional-based one.