What good practice when throwing exception within if?

25

What is the best practice in this case, to use else even though you know that if will throw an exception or not?

option 1 (with else ):

if (condicao){
  throw new RuntimeException("Mensagem");
}else{
  System.out.println("Não deu exception");
}

option 2 (without else ):

if (condicao){
  throw new RuntimeException("Mensagem");
}
System.out.println("Não deu exception");
    
asked by anonymous 14.01.2014 / 11:28

4 answers

14

While this is prone to a strong sense of personal opinion, I believe best practice is not to use block else .

The reasons are as follows:

Code Readability

Adding nested blocks, although in this small example does not make a big impact, will only make it difficult to read the code and make it more complex.

In practice, this can lead to situations with multiple levels of validation, as I have sometimes seen in systems out there:

if (condicao1){
    throw new RuntimeException("Mensagem 1");
}else{
    System.out.println("Não deu exception 1");
    if (condicao1){
        throw new RuntimeException("Mensagem 2");
    }else{
        System.out.println("Ainda não deu exception 2");
        if (condicao1){
            throw new RuntimeException("Mensagem 3");
        }else{
            System.out.println("Ainda não deu exception 3");
        }
    }
}

For those who think that I am exaggerating, I have not once or twice seen methods with validations of 5, 6 or more levels in methods of financial systems with somewhat complex business rules.

No need

A condition that includes an exception throw, or other statement that stops the execution of the method does not need this type of build. Unnecessary code is waste of time.

It is better to separate the initial validations

The initial validations of a method do not need and probably should not influence the main logic of the method.

public int meuMetodo(String parametro) {

    if (parametro == null || parametro.isEmpty()) 
        throw new IllegalArgumentException("Parâmetro não informado!");

    //lógica aqui
    return 123;

}
    
14.01.2014 / 12:05
12

From the standpoint of performance, there is no reason to choose between one and the other, the difference must be negligible. Although the representation of the two forms in the bytecode is different, the performance will be very similar , with a difference of a few cycles at most (by contrast, a miss miss in the L1 "wastes" 10-40 cycles, in the L2 more than 600).

From the standpoint of readability, I believe it will depend on the semantics of your program. If the if condition refers to an exceptional case, I think it is best to omit else :

if ( denominador == 0 )
    throw new RuntimeException("Não pode dividir por zero");
quociente = numerador / denominador;
On the other hand, if the if condition is part of a mutually exclusive set, then I think it would be interesting to include else (or maybe even else if ):

if ( x < 10 )
    return foo(x);
else if ( 10 <= x && x <= 20 )
    throw new RuntimeException("x não pode estar dentro do intervalo");
else if ( 20 < x )
    return bar(x);
// else
//     throw new RuntimeException("Isso nunca deve acontecer");
    
14.01.2014 / 12:00
7

Your if is a "guard clause": a clause that, if true, prevents the execution of the rest of the method code (or a loop). Guard clauses always contain "jump" code, such as return , throw , break or continue . They serve as preconditions for running certain blocks of code.

My personal opinion is that these clauses do not need else , since the code will "jump" out even:

if (condicao) throw new RuntimeException("Oops");

.... resto do código

Imagine a method with many preconditions, using else would make the code more unreadable.

Note that I also do the following for "Guard Clauses":

  • I leave the code for if and "leap" on the same line
  • I do not use a block delimited with {}

See a answer to a similar question I made in the OS.

In the classic book Refactoring by Martin Fowler, there is a refactoring to turn from nested conditionals to guard clauses .

    
14.01.2014 / 14:52
5

In my opinion, the less context the better. In general, a different context is started with { and finalized with } , and from the point of view of who reads the code, it is equivalent to things of the past that must be remembered so that the code can be understood. >

For example:

public int metodo(int parametro) {
    if (parametero < 0) {
        throw new IllegalArgumentException();
    } else {
        // Estamos no contexto do else que está dentro do contexto do método.
        // Ou seja, você tem que ter estes dois contextos em mente.
        return 42 + parametro;
    }
}

Already here:

public int metodo(int parametro) {
    if (parametero < 0) {
        throw new IllegalArgumentException();
    }

    // Estamos no contexto do método, fora de qualquer if ou else.
    return 42 + parametro;
}

This becomes quite evident in codes like this:

public boolean validaCPF(String cpf) {
    if (cpf == null || cpf.length() != 11) {
        return false;
    } else {
        int a = 0, b = 0;
        for (int i = 0; i < 9; i++) {
            int c = cpf.charAt(i) - '0';
            if (c < 0 || c > 9) {
                return false;
            } else {
                a += c * (i + 1);
                b += c * (9 - i);
            }
        }
        a = (a % 11) % 10;
        b = (b % 11) % 10;
        if (cpf.charAt(9) - '0' == a && cpf.charAt(10) - '0' == b) {
            return true;
        } else {
            return false;
        }
    }
}

That looks so much better:

public boolean validaCPF(String cpf) {
    if (cpf == null || cpf.length() != 11) return false;

    int a = 0, b = 0;
    for (int i = 0; i < 9; i++) {
        int c = cpf.charAt(i) - '0';
        if (c < 0 || c > 9) return false;
        a += c * (i + 1);
        b += c * (9 - i);
    }
    a = (a % 11) % 10;
    b = (b % 11) % 10;
    return cpf.charAt(9) - '0' == a && cpf.charAt(10) - '0' == b;
}

This demonstrates that if the else can be avoided, the code gets cleaner.

    
14.01.2014 / 20:07