Por que evitar usar exceções para controlar seu fluxo em Java

Java é uma linguagem de programação universal que possui muitas soluções alternativas para suas tarefas específicas. No entanto, existem boas abordagens que devem ser seguidas e também existem algumas que não são bem-sucedidas e que ainda usamos a maior parte do tempo.

Uma das abordagens com falha mais comuns é usar exceções para controlar o fluxo de execução. Isso deve ser evitado por dois motivos:

  1. Isso reduz o desempenho e a velocidade do seu código.
  2. Isso torna seu código menos legível.

Vamos começar vendo um exemplo. Aqui, a exceção é usada para controlar o fluxo de execução:

public static int findAge(String name) { try { String ageAsString = findUser(name); return ageAsString.length(); } catch (NameNotFoundException e) { return 0; } } private static String findUser(String name) { if(name==null) { throw new NameNotFoundException(); } return name; } 

Se o usuário fornecer um valor não nulo para o nome, o método findAge retornará o tamanho desse nome, mas se o nome do usuário for nulo, o método findUser lançará uma exceção NameNotFoundException e, nesse caso, o método findAge retornará 0.

Como podemos converter esse código sem usar exceções? Em geral, existem muitas opções, considere uma delas:

 public static int findAgeNoEx(String name) { String ageAsString = findUserNoEx(name); return ageAsString.length(); } private static String findUserNoEx(String name) { if(name==null) { return ""; } return name; } 

Para descobrir o impacto da exceção no desempenho, preparei o seguinte código que chama as duas variantes dos métodos 10 milhões de vezes: com e sem exceção.

 public class ControlFlowWithExceptionOrNot { public static class NameNotFoundException extends RuntimeException { private static final long serialVersionUID = 3L; } private static final int TRIAL = 10000000; public static void main(String[] args) throws InterruptedException { long start = System.currentTimeMillis(); for (int i = 0; i < TRIAL; i++) { findAgeNoEx(null); } System.out.println("Duration :" + (System.currentTimeMillis() - start)); long start2 = System.currentTimeMillis(); for (int i = 0; i < TRIAL; i++) { findAge(null); } System.out.println("Duration :" + (System.currentTimeMillis() - start2)); }; public static int findAge(String name) { try { String ageAsString = findUser(name); return ageAsString.length(); } catch (NameNotFoundException e) { return 0; } } private static String findUser(String name) { if (name == null) { throw new NameNotFoundException(); } return name; } public static int findAgeNoEx(String name) { String ageAsString = findUserNoEx(name); return ageAsString.length(); } private static String findUserNoEx(String name) { if (name == null) { return ""; } return name; } } 

Conclusão:

 Duration :16 Duration :6212 

Como você pode ver, o uso da exceção nos custou milhares de milissegundos no meu Intel Core i7-3630QM.

Se compararmos nossos dois métodos findAge em termos de legibilidade, o método sem exceção é absolutamente claro: primeiro, podemos ter certeza absoluta de que o método findUser retorna uma string; e segundo, independentemente de qual sequência será retornada, obtemos seu comprimento. Ao mesmo tempo, o método com a exceção é um pouco confuso: não está completamente claro o que exatamente o método findUser retorna. Ele pode retornar uma string ou lançar uma exceção e isso não é visível na assinatura do método. Por esse motivo, o paradigma de programação funcional não aceita o uso de exceções.

No final, seria melhor se você usar exceções onde a exceção realmente surge e é necessária. Se você usar exceções para controlar o fluxo de execução, isso causará uma queda no desempenho do programa e tornará seu código menos legível.

Espero que este artigo seja interessante para você e possivelmente útil.

Source: https://habr.com/ru/post/pt476142/


All Articles