0

I use exceptions to flow control a lot, but I have a strange feeling that I am doing something wrong. Is it a good practice to write something like code shown bellow?

public static void main(String[] args)
{
    try
    {
        methodA();
    }
    catch (Exception ex)
    {
        handleException(ex);
    }
}

public static methodA()
{
    methodB();
}

public static methodB()
{
    if(someConditionIsNotMet)
    {
        throw new RuntimeException("Some Condition Is Not Met");
    }
}
Milorad
  • 152
  • 2
  • 10

3 Answers3

1

I use exceptions to flow control a lot

Throwing specific and functional exceptions to indicate a functional issue during the workflow is not bad itself.
It is not the single way to do it but it is a valid way.
Another way relies on methods that returns boolean and testing the returned value.
Personally, I don't use this way as I found it rather verbose, error-prone (we have not to forget to test the returned boolean) and less expressive (it has only two values: true and false) than an exception (it may have as many as required).

Suppose that method B has to check something and that if the check fails the processing should be stopped and the client notified of the issue, it would be totally valid to use exceptions for this purpose.
Now, it would make more sense to make the exception a specific exception rather than Exception.
Otherwise how the client could interpret the exception meaning ?
The exception could be a workflow exception but it could be also any exception thrown at runtime for another reason such as a NullPointerException.
You want to handle workflow exceptions in a specific way while you will not apply a specific processing to other thrown exceptions.

For example you could write something as :

public static methodA()
{
    methodB();
}

public static methodB(){
    if (!expectedDataFound()){
      throw new DataNotFoundException("data xxx was not found");
    }
    if (!hasRights()){
      throw new PermissionException("user xxx has not the rights for yyy");
    }
}

Then from the client side, you have two ways.
Catching each exception individually or catching them in a common way (that is possible only if they make part of the same hierarchy).

Catching each exception individually :

public static void main(String[] args)
{
    try
    {
        methodA();
    }
    catch (DataNotFoundException ex)
    {
        handleDataNotFoundException(ex);
    }
    catch (PermissionException ex)
    {
        handlePermissionException(ex);
    }
}

Catching exception globally:

public static void main(String[] args)
{
    try
    {
        methodA();
    }
    catch (WorkflowException ex)
    {
        handleWorkflowException(ex);
    }
}
davidxxx
  • 125,838
  • 23
  • 214
  • 215
1

I think you are too harsh on yourself with saying that you "use exceptions to control flow". It is an antipattern to use exceptions for control flow, but in your example you do not.

Let's say that you have a method that sets the age for the user, and of course if the caller provided negative number, you should not complete the action. So a very reasonable way to ensure that, would be:

public void setAge(int age) {
    if(age <0) {
        throw new InvalidArgumentException("Age has to be zero or positive number");
    }
}

If you prefer not to use exceptions maybe you can use features of the language such as Optionals or create response structure that handles both success and errors. For example, lets say you have a method that retrieves employees

public EmployeesOverview getEmployees() { ... }

Your response class could look like this:

public class EmployeesOverview {
    private Ok ok;
    private Error error;

    class Ok {
        private Set<Employee> employees;
    }

    class Error {
        private String errorMessage;
    }
}

So without throwing exception your method will provide clients with results or if there is a problem, the client will be informed about it.

martidis
  • 2,897
  • 1
  • 11
  • 13
0

I would separate flow control and exception handling. Flow control is meant for making sure statements are executed in correct sequence and under correct conditions. This must be determined at design time. Exception handling is meant to handle unforeseen situations at run time. Exceptions are almost always due to external factors: time outs, no disk space, data errors...

Just my two cents.