Clojure - tratamento de exceções

Exception handlingé necessário em qualquer linguagem de programação para lidar com os erros de tempo de execução de forma que o fluxo normal do aplicativo possa ser mantido. A exceção geralmente interrompe o fluxo normal do aplicativo, razão pela qual precisamos usar o tratamento de exceção em nosso aplicativo.

A exceção é amplamente classificada nas seguintes categorias -

  • Checked Exception- As classes que estendem a classe Throwable, exceto RuntimeException e Error, são conhecidas como exceções verificadas. Por exemplo, IOException, SQLException, etc. As exceções verificadas são verificadas em tempo de compilação.

Vamos considerar o programa a seguir, que executa uma operação em um arquivo chamado Example.txt. No entanto, pode sempre haver um caso em que o arquivo Example.txt não existe.

(ns clojure.examples.example
   (:gen-class))

;; This program displays Hello World
(defn Example []
   (def string1 (slurp "Example.txt"))
   (println string1))
(Example)

Se o arquivo Example.txt não existir, a seguinte exceção será gerada pelo programa.

Caused by: java.io.FileNotFoundException: Example.txt (No such file or
directory)
at java.io.FileInputStream.open0(Native Method)
at java.io.FileInputStream.open(FileInputStream.java:195)
at java.io.FileInputStream.<init>(FileInputStream.java:138)
at clojure.java.io$fn__9185.invoke(io.clj:229)
at clojure.java.io$fn__9098$G__9091__9105.invoke(io.clj:69)
at clojure.java.io$fn__9197.invoke(io.clj:258)
at clojure.java.io$fn__9098$G__9091__9105.invoke(io.clj:69)

A partir da exceção acima, podemos ver claramente que o programa gerou uma FileNotFoundException.

  • Unchecked Exception- As classes que estendem RuntimeException são conhecidas como exceções não verificadas. Por exemplo, ArithmeticException, NullPointerException, ArrayIndexOutOfBoundsException, etc. As exceções não verificadas não são verificadas em tempo de compilação, em vez disso, são verificadas em tempo de execução.

Um caso clássico é a ArrayIndexOutOfBoundsException que ocorre quando você tenta acessar um índice de uma matriz que é maior do que o comprimento da matriz. A seguir está um exemplo típico desse tipo de erro.

(ns clojure.examples.example
   (:gen-class))
(defn Example []
   (try
      (aget (int-array [1 2 3]) 5)
      (catch Exception e (println (str "caught exception: " (.toString e))))
      (finally (println "This is our final block")))
   (println "Let's move on"))
(Example)

Quando o código acima é executado, a seguinte exceção será levantada.

caught exception: java.lang.ArrayIndexOutOfBoundsException: 5
This is our final block
Let's move on

Erro

O erro é irrecuperável, por exemplo, OutOfMemoryError, VirtualMachineError, AssertionError, etc. Estes são erros dos quais o programa nunca pode se recuperar e farão com que o programa trave. Agora precisamos de algum mecanismo para capturar essas exceções para que o programa possa continuar a ser executado se essas exceções existirem.

O diagrama a seguir mostra como a hierarquia de exceções em Clojure é organizada. É tudo baseado na hierarquia definida em Java.

Captura de exceções

Assim como outras linguagens de programação, Clojure fornece o bloco normal 'try-catch' para capturar exceções como e quando elas ocorrem.

A seguir está a sintaxe geral do bloco try-catch.

(try
   (//Protected code)
   catch Exception e1)
(//Catch block)

Todo o seu código que poderia gerar uma exceção é colocado no Protected code block.

No catch block, você pode escrever um código personalizado para lidar com sua exceção para que o aplicativo possa se recuperar da exceção.

Vejamos nosso exemplo anterior que gerou uma exceção de arquivo não encontrado e ver como podemos usar o bloco try catch para capturar a exceção levantada pelo programa.

(ns clojure.examples.example
   (:gen-class))
(defn Example []
   (try
      (def string1 (slurp "Example.txt"))
      (println string1)
      (catch Exception e (println (str "caught exception: " (.getMessage e))))))
(Example)

O programa acima produz a seguinte saída.

caught exception: Example.txt (No such file or directory)

A partir do código acima, resolvemos o código defeituoso no try block. No bloco catch, estamos apenas capturando nossa exceção e enviando uma mensagem de que ocorreu uma exceção. Portanto, agora temos uma maneira significativa de capturar a exceção, que é gerada pelo programa.

Vários blocos de captura

Pode-se ter vários blocos catch para lidar com vários tipos de exceções. Para cada bloco catch, dependendo do tipo de exceção levantada, você escreveria o código para tratá-la de acordo.

Vamos modificar nosso código anterior para incluir dois blocos catch, um que é específico para nossa exceção de arquivo não encontrado e o outro é para um bloco de exceção geral.

(ns clojure.examples.example
   (:gen-class))
(defn Example []
   (try
      (def string1 (slurp "Example.txt"))
      (println string1)
      
      (catch java.io.FileNotFoundException e (println (str "caught file
         exception: " (.getMessage e))))
      
      (catch Exception e (println (str "caught exception: " (.getMessage e)))))
   (println "Let's move on"))
(Example)

O programa acima produz a seguinte saída.

caught file exception: Example.txt (No such file or directory)
Let's move on

A partir da saída acima, podemos ver claramente que nossa exceção foi capturada pelo bloco de captura 'FileNotFoundException' e não pelo geral.

Finalmente Bloco

O bloco finalmente segue um bloco try ou um bloco catch. Um bloco final de código sempre é executado, independentemente da ocorrência de uma Exceção.

Usar um bloco finally permite que você execute quaisquer instruções do tipo de limpeza que deseja executar, não importa o que aconteça no código protegido. A seguir está a sintaxe para este bloco.

(try
   (//Protected code)
   catch Exception e1)
(//Catch block)
(finally
   //Cleanup code)

Vamos modificar o código acima e adicionar o bloco finally de código. A seguir está o trecho de código.

(ns clojure.examples.example
   (:gen-class))
(defn Example []
   (try
      (def string1 (slurp "Example.txt"))
      (println string1)
      
      (catch java.io.FileNotFoundException e (println (str "caught file
         exception: " (.getMessage e))))
      
      (catch Exception e (println (str "caught exception: " (.getMessage e))))
      (finally (println "This is our final block")))
   (println "Let's move on"))
(Example)

O programa acima produz a seguinte saída.

caught file exception: Example.txt (No such file or directory)
This is our final block
Let's move on

No programa acima, você pode ver que o bloco final também é implementado depois que o bloco catch captura a exceção necessária.

Como Clojure deriva seu tratamento de exceções de Java, semelhante a Java, os seguintes métodos estão disponíveis em Clojure para gerenciar as exceções.

  • public String getMessage()- Retorna uma mensagem detalhada sobre a exceção que ocorreu. Esta mensagem é inicializada no construtor Throwable.

  • public Throwable getCause() - Retorna a causa da exceção representada por um objeto Throwable.

  • public String toString() - Retorna o nome da classe concatenada com o resultado de getMessage ().

  • public void printStackTrace() - Imprime o resultado de toString () junto com o rastreamento de pilha em System.err, o fluxo de saída de erro.

  • public StackTraceElement [] getStackTrace()- Retorna uma matriz contendo cada elemento no rastreamento de pilha. O elemento no índice 0 representa o topo da pilha de chamadas e o último elemento da matriz representa o método na parte inferior da pilha.

  • public Throwable fillInStackTrace() - Preenche o rastreamento da pilha deste objeto Throwable com o rastreamento da pilha atual, adicionando a qualquer informação anterior no rastreamento da pilha.

A seguir está o código de exemplo que usa alguns dos métodos listados acima.

(ns clojure.examples.example
   (:gen-class))
(defn Example []
   (try
      (def string1 (slurp "Example.txt"))
      (println string1)
      
      (catch java.io.FileNotFoundException e (println (str "caught file
         exception: " (.toString e))))
      
      (catch Exception e (println (str "caught exception: " (.toString e))))
   (finally (println "This is our final block")))
   (println "Let's move on"))
(Example)

O programa acima produz a seguinte saída.

caught file exception: java.io.FileNotFoundException: Example.txt (No such file
or directory)
This is our final block
Let's move on