3

In a service daemon coded in Java, we have services for getting various objects. When we use these services forgetting an object, I wonder if

  • the method should return the object and throw an exception when It's not found
  • or return an Optional (this is what we opted for, so far)

Mind you , this service is not for a web service, so I have no use for a Global Exception Handler or such

class MyService
    public A getA(String key){
        ...
        // if A not found
        throw NotFoundException()
    }

vs

class MyService
    public Optional<A> getA(String key){
        ...
        // if A not found
        return Optional.empty()
    }
8
  • 5
    Required reading: Exceptions: The Right Way tl;dr: both approaches are right Commented May 21 at 16:28
  • At some point we have to clear out what an "opinion based" question means, cause this question can have perfectly valid answers for OP to pick from.
    – Ccm
    Commented May 21 at 19:56
  • 2
    @Ccm: the reason I believe this is opinion-based is you can have two different answers which are equally valid, especially given the vague information in this question. If the OP can edit their question to include more information about the use case and the code, I would consider voting to reopen. Commented May 21 at 20:06
  • 1
    @GregBurghardt I am pretty sure the majority of questions here fall in that category.
    – Ccm
    Commented May 21 at 20:07
  • Eh. I voted to reopen. The code might be generic, but this question is further constrained by it running as a service as opposed to a web API or server. And further constrained to "finding something". Commented May 21 at 20:09

4 Answers 4

4

There are two primary use cases for exceptions, which partially overlap:

  1. You detect a problem that is unrelated to why this function might be called. It is unlikely that the calling code knows any better how to deal with this problem. Examples are out-of-memory conditions, or network errors where the calling code isn't aware that network communication is being used, etc. In these cases, exceptions can be used to perform a multi-level jump up the call-stack to a function that might be able to deal with the problem.

  2. You detect a "this should not happen" problem, which is indicative of a bug in the system. Here, exceptions are used to halt the faulty calculations in their tracks. An example could be a retrieval function (getA), where it is known that the function can/should only be called with the id of an existing object.

For your example, both throwing an exception and returning an Optional are valid, but if it can somehow be expected that an ID gets passed in that doesn't correspond to an object, then the usual preference is to return an Optional.

1

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.

1

throwing an exception or returning Optional

Exception and Optional offer different information. Using one or the other depends on whether what has to be done can be done or not with the information each of the two offers.

Use Optional when existence/non-existence of information suffices to go through the program flow. Example: Presume there is an object requiring type information. Using Optional it can be decided that objects missing type information has to be processed differently than objects including the type information.

Use Exception for a fine grained control of the program flow. Example: Presume there is an object requiring type information and type information must transition according to a state diagram. Use Exception to steer the transition between supported values.

-1

The rule is that you throw exceptions in exceptional circumstances. For example, if the user enters a part number and you look up information about that part and return it, then it is normal that the user enters a wrong part code and nothing is found, no exception.

If every employee has a GUID and you can lookup further information about the employee by GUID then that GUID should always be correct. Throw an exception if the GUID doesn’t exist; there is very likely a bug to be fixed. On the other hand, if you don’t get the information because the internet connection is down, that is not an exception.

28
  • 6
    I've come to realize that the problem with the phrase "exceptions are for exceptional circumstances" is that it doesn't really help someone determine when to use them. It's pretty common to use exceptions for a network failure, but that's not an exceptional circumstance. I think a better way to think about it is analogous to an 'exception to the rule'. That is, for things that are awkward to handle in the normal control flow.
    – JimmyJames
    Commented May 21 at 18:33
  • 1
    @JimmyJames, and it is even more nuanced than that. Exceptions stop the flow. And then when do you pull the pin on the hand grenade versus just returning early? In fact, I like to think of throwing an exception as "pulling the pin on a hand grenade." When should you just blow it up. Scuttle the ship. Walk away from this dumpster fire and wash your hands of it. That's when it's appropriate to throw an exception. And then, when to catch an exception? Well, when can you save the ship? What's the cost of letting your program crash? Such a complex thing masquerading as a simple throw. Commented May 21 at 18:44
  • 4
    Trying to restrict exceptions for exceptional circumstances often doesn't work in practice. Exceptions are really just a way of de-coupling the code which detects a problem away from the code which is able to handle that problem. That could be a simple problem related to bad input, or an infrastructure problem. A lot of modern libraries and frameworks in a lot of languages have realised this and taken advantage of it to help clean up their interfaces, so in many cases the use of exceptions is already decided elsewhere. Commented May 21 at 19:20
  • 1
    NaNs decided not to use exceptions “elsewhere”. Worked out fine. You certainly can reasonably avoid exceptions in most cases. Just remember you have to be able to reasonably explain what you did to new coders. Commented May 21 at 19:47
  • 1
    @candied_orange NaNs worked out fine? I think they are viral information destroyers. Like prions for software.
    – JimmyJames
    Commented May 21 at 20:09

Not the answer you're looking for? Browse other questions tagged or ask your own question.