Clojure - Gestione delle eccezioni

Exception handlingè richiesto in qualsiasi linguaggio di programmazione per gestire gli errori di runtime in modo che il normale flusso dell'applicazione possa essere mantenuto. L'eccezione di solito interrompe il normale flusso dell'applicazione, motivo per cui è necessario utilizzare la gestione delle eccezioni nella nostra applicazione.

L'eccezione è ampiamente classificata nelle seguenti categorie:

  • Checked Exception- Le classi che estendono la classe Throwable tranne RuntimeException ed Error sono note come eccezioni controllate. Ad esempio IOException, SQLException, ecc. Le eccezioni selezionate vengono verificate in fase di compilazione.

Consideriamo il seguente programma che esegue un'operazione su un file chiamato Example.txt. Tuttavia, può sempre esserci un caso in cui il file Example.txt non esiste.

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

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

Se il file Example.txt non esiste, la seguente eccezione verrà generata dal programma.

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)

Dall'eccezione precedente, possiamo vedere chiaramente che il programma ha generato un'eccezione FileNotFoundException.

  • Unchecked Exception- Le classi che estendono RuntimeException sono note come eccezioni non controllate. Ad esempio, ArithmeticException, NullPointerException, ArrayIndexOutOfBoundsException, ecc. Le eccezioni non selezionate non vengono verificate in fase di compilazione, piuttosto vengono controllate in fase di esecuzione.

Un caso classico è ArrayIndexOutOfBoundsException che si verifica quando si tenta di accedere a un indice di un array che è maggiore della lunghezza dell'array. Di seguito è riportato un tipico esempio di questo tipo di errore.

(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 viene eseguito il codice precedente, verrà sollevata la seguente eccezione.

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

Errore

L'errore è irrecuperabile, ad esempio OutOfMemoryError, VirtualMachineError, AssertionError, ecc. Ora abbiamo bisogno di un meccanismo per catturare queste eccezioni in modo che il programma possa continuare a funzionare se queste eccezioni esistono.

Il diagramma seguente mostra come è organizzata la gerarchia delle eccezioni in Clojure. È tutto basato sulla gerarchia definita in Java.

Catturare le eccezioni

Proprio come altri linguaggi di programmazione, Clojure fornisce il normale blocco "try-catch" per catturare le eccezioni come e quando si verificano.

Di seguito è riportata la sintassi generale del blocco try-catch.

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

Tutto il codice che potrebbe sollevare un'eccezione viene inserito nel file Protected code block.

Nel catch block, puoi scrivere codice personalizzato per gestire l'eccezione in modo che l'applicazione possa eseguire il ripristino dall'eccezione.

Diamo un'occhiata al nostro esempio precedente che ha generato un'eccezione file non trovato e vediamo come possiamo usare il blocco try catch per catturare l'eccezione sollevata dal programma.

(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)

Il programma precedente produce il seguente output.

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

Dal codice sopra, escludiamo il codice difettoso nel file try block. Nel blocco catch, stiamo solo catturando la nostra eccezione e inviando un messaggio che si è verificata un'eccezione. Quindi, ora abbiamo un modo significativo per catturare l'eccezione, che viene generata dal programma.

Blocchi multipli di cattura

Si possono avere più blocchi catch per gestire più tipi di eccezioni. Per ogni blocco catch, a seconda del tipo di eccezione sollevata, dovresti scrivere codice per gestirlo di conseguenza.

Modifichiamo il nostro codice precedente per includere due blocchi catch, uno specifico per l'eccezione file non trovato e l'altro per un blocco eccezione generale.

(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)

Il programma precedente produce il seguente output.

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

Dall'output precedente, possiamo vedere chiaramente che la nostra eccezione è stata catturata dal blocco catch "FileNotFoundException" e non da quello generale.

Infine Block

Il blocco finalmente segue un blocco try o un blocco catch. Un blocco di codice infine viene sempre eseguito, indipendentemente dal verificarsi di un'eccezione.

L'utilizzo di un blocco finalmente consente di eseguire qualsiasi istruzione di tipo cleanup che si desidera eseguire, indipendentemente da ciò che accade nel codice protetto. Di seguito è riportata la sintassi per questo blocco.

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

Modifichiamo il codice sopra e aggiungiamo il blocco di codice finalmente. Di seguito è riportato lo snippet di codice.

(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)

Il programma precedente produce il seguente output.

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

Dal programma sopra, puoi vedere che il blocco finale viene implementato anche dopo che il blocco catch ha catturato l'eccezione richiesta.

Poiché Clojure deriva la sua gestione delle eccezioni da Java, in modo simile a Java, i seguenti metodi sono disponibili in Clojure per la gestione delle eccezioni.

  • public String getMessage()- Restituisce un messaggio dettagliato sull'eccezione che si è verificata. Questo messaggio viene inizializzato nel costruttore Throwable.

  • public Throwable getCause() - Restituisce la causa dell'eccezione rappresentata da un oggetto Throwable.

  • public String toString() - Restituisce il nome della classe concatenata con il risultato di getMessage ().

  • public void printStackTrace() - Stampa il risultato di toString () insieme alla traccia dello stack su System.err, il flusso di output dell'errore.

  • public StackTraceElement [] getStackTrace()- Restituisce un array contenente ogni elemento nella traccia dello stack. L'elemento all'indice 0 rappresenta la parte superiore dello stack di chiamate e l'ultimo elemento dell'array rappresenta il metodo in fondo allo stack di chiamate.

  • public Throwable fillInStackTrace() - Riempie la traccia dello stack di questo oggetto Throwable con la traccia dello stack corrente, aggiungendosi a qualsiasi informazione precedente nella traccia dello stack.

Di seguito è riportato il codice di esempio che utilizza alcuni dei metodi sopra elencati.

(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)

Il programma precedente produce il seguente output.

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