Caroline
Caroline

Reputation: 412

How to throw an Exception inside a Try/Catch block?

I have the following Java method:

public Class createClass(Class class) {
    try {
        // retrieve the professor of the class and check if he exists
        Professor professorFound = professorRepository.findById(class.getProfessorId());
        if (professorFound != null) {
           // if the professor exists, then check if he already has a class
           // with the same id
           List<Class> classes = professorFound.getClasses();
           List<Class> classFound = classes.stream().... // loop to find the class... 

           // if he has, throw an exception
           if(!classFound.isEmpty()) {
                throw new ClassAlreadyRegisteredException();

           } else {
                // if he does not have, then create the class
                Class class = new Class();
                professorFound.getClasses().add(class);
                return professorRepository.save(professorFound);
           }
        } else {
           // if the professor does not exist, throw an exception
           throw new ProfessorNotFoundException();
    } catch (Exception e) {
        // if there is any other error during the communication with the database, 
        // throw a generic IOException
        throw new ClassIOException();
    }
}

Basically, what I need is to throw specific Exceptions (if the professor informed in the request does not exist, or if the professor already has a class with the same id), or throw a generic IOException if there is any other error during the communication with the database.

However, in the way that I have developed, if any specific Exception is thrown, the try block will catch the exception and will throw a generic IOException.

How can I solve this problem?

I'm very interested in understanding what are the best practices in this case. Should I catch each specific exception separately and throw them twice? Is that a good practice?

EDIT:

This is how my ClassAlreadyRegisteredException looks like:

public class ClassAlreadyRegisteredException extends ApiException {

    private static final long serialVersionUID = 1L;

    public ClassAlreadyRegisteredException(String code, String message, String developerMessage, String origin, HttpStatus status) {
        super(code,message,developerMessage,origin, status);
    }
}

This is how my ApiException looks like:

@Data
@AllArgsConstructor
@RequiredArgsConstructor
@EqualsAndHashCode(callSuper = false)
public class ApiException extends RuntimeException{
    
    private static final long serialVersionUID = 1L;
    
    private String code;
    private String userMessage;
    private String developerMessage;
    private String origin;
    private HttpStatus status;
}

Thanks in advance.

Upvotes: 1

Views: 10763

Answers (2)

J.Backus
J.Backus

Reputation: 1441

Catch and re-throw.

try {
   ... same as before ...
} catch (ClassAlreadyRegisteredException | ProfessorNotFoundException e) {
    throw e;
} catch (Exception e) {
    // if there is any other error during the communication with the database, 
    // throw a generic IOException
    throw new ClassIOException();
}

Alternatively, remember the exception to throw it later.

 Exception fail = null;

 try {
      ….
     // save exception in place of existing throws
     // for example:
     fail = new ClassAlreadyRegisteredException();
     …
 } catch (Exception ex) {
     ...same as original...
 }

 if (fail != null) {
     throw fail;
 }

I use both techniques; the choice depends on what is simpler in any given situation. Neither is uncontestably better.

For the catch and re-throw method, you have to keep the list of caught-and-rethrown exceptions consistent with the exceptions you actually throw from within the try-clause. In larger cases, I'd avoid that problem by using an exception hierarchy, so I could catch the common base class.

For the save-and-throw method, you have to arrange control flow so that nothing significant is done after detecting the failure, since you don't have the immediate 'throw' command to exit the try-clause. Nevertheless there are cases where it is simple enough; the original example is one such.

Upvotes: 6

shinjw
shinjw

Reputation: 3421

Checked vs Unchecked Exceptions

It's totally acceptable to throw an exception in a catch block. A common use case is to take a checked Exception and throw a unchecked RuntimeException which would allow the exception bubble up to where it needs to be handled.

You'll want to use checked exceptions for use cases such as Connectivity/IO, SQL exceptions..

Handling Checked Exceptions

To answer your question, in most libraries that connect to the database, an checked IOException is thrown if there are any connectivity issues. For these cases, you can always specify this in the method signature public Class createClass() throws IOException

this specifies that any callers of createClass() has to fulfill the contract that the IOException is handled.

or

You can rethrow this as a RuntimeException

try {
   ...
} catch (IOException e) {
   throw new RuntimeException(e); // <- send the original exception so you can preserve the exception and stacktrace.
}

This will essentially bubble up to STDOUT or whatever handler your framework specifies.

CAUTION:

However, catching an cover all Exception and throwing a more specific ClassIOException can have unintended consequences.

If you have a NullPointerException this will be captured by your catch (Exception e) block and rethrown as a ClassIOException

Doing this will corrupt the stacktrace and cause your error logs to be much more difficult to debug.

Understanding what constitutes an checked Exceptions.

Another tip is to consider what your Exception cases are.

If they are standard flow of the application, service, or business logic -- these may not be appropriate exceptions.

The ClassAlreadyRegisteredException and ProfessorNotFoundException may not be exceptional cases in your application... unless these are already specified by your professor.

Sometimes these can be thrown as RuntimeException if the situation calls for it.

Best Practices?

There are still many opinions on how exceptions are handled. So here are some rule of thumb questions I ask myself when working with exceptions

  1. Is the stacktrace preserved? Will I be able to trace back to the root Exception
  2. Is this common logic and represents an exceptional case that deviates from what my application provides?
  3. If I throw this exception, is it an absolute necessity for anything calling this method to handle this exceptional logic?

Upvotes: 2

Related Questions