Clojure - obsługa wyjątków

Exception handlingjest wymagany w każdym języku programowania do obsługi błędów wykonania, tak aby można było zachować normalny przepływ aplikacji. Wyjątki zwykle zakłócają normalny przepływ aplikacji, co jest powodem, dla którego musimy korzystać z obsługi wyjątków w naszej aplikacji.

Wyjątek można ogólnie podzielić na następujące kategorie -

  • Checked Exception- Klasy, które rozszerzają klasę Throwable z wyjątkiem RuntimeException i Error, są nazywane sprawdzanymi wyjątkami. Np. IOException, SQLException, itp. Zaznaczone wyjątki są sprawdzane w czasie kompilacji.

Rozważmy następujący program, który wykonuje operację na pliku o nazwie Example.txt. Jednak zawsze może się zdarzyć, że plik Example.txt nie istnieje.

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

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

Jeśli plik Example.txt nie istnieje, to program wygeneruje następujący wyjątek.

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)

Z powyższego wyjątku wyraźnie widać, że program zgłosił wyjątek FileNotFoundException.

  • Unchecked Exception- Klasy, które rozszerzają RuntimeException, są nazywane niezaznaczonymi wyjątkami. Na przykład ArithmeticException, NullPointerException, ArrayIndexOutOfBoundsException itp. Niezaznaczone wyjątki nie są sprawdzane w czasie kompilacji, ale są sprawdzane w czasie wykonywania.

Jednym z klasycznych przypadków jest wyjątek ArrayIndexOutOfBoundsException, który ma miejsce, gdy próbujesz uzyskać dostęp do indeksu tablicy, który jest większy niż długość tablicy. Poniżej znajduje się typowy przykład tego rodzaju błędu.

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

Po wykonaniu powyższego kodu zostanie zgłoszony następujący wyjątek.

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

Błąd

Błąd jest nieodwracalny, np. OutOfMemoryError, VirtualMachineError, AssertionError, itp. Są to błędy, z których program nigdy nie może odzyskać i spowodują zawieszenie programu. Potrzebujemy teraz mechanizmu do wychwytywania tych wyjątków, aby program mógł nadal działać, jeśli te wyjątki istnieją.

Poniższy diagram pokazuje, jak zorganizowana jest hierarchia wyjątków w Clojure. Wszystko opiera się na hierarchii zdefiniowanej w Javie.

Łapanie wyjątków

Podobnie jak inne języki programowania, Clojure zapewnia normalny blok „try-catch” do wychwytywania wyjątków w momencie ich wystąpienia.

Poniżej znajduje się ogólna składnia bloku try-catch.

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

Cały twój kod, który może zgłosić wyjątek, jest umieszczony w Protected code block.

w catch block, możesz napisać niestandardowy kod do obsługi wyjątku, tak aby aplikacja mogła odzyskać dane po wyjściu.

Spójrzmy na nasz wcześniejszy przykład, który wygenerował wyjątek nie znaleziono pliku i zobaczmy, jak możemy użyć bloku try catch do przechwycenia wyjątku zgłoszonego przez program.

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

Powyższy program generuje następujące dane wyjściowe.

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

Z powyższego kodu zawijamy błędny kod w try block. W bloku catch po prostu przechwytujemy nasz wyjątek i wyświetlamy komunikat, że wystąpił wyjątek. Mamy więc teraz sensowny sposób przechwytywania wyjątku, który jest generowany przez program.

Wiele bloków połowowych

Można mieć wiele bloków catch do obsługi wielu typów wyjątków. Dla każdego bloku catch, w zależności od typu zgłoszonego wyjątku, napisałbyś kod, aby odpowiednio go obsłużyć.

Zmodyfikujmy nasz wcześniejszy kod, aby zawierał dwa bloki catch, jeden specyficzny dla naszego pliku nie znaleziono wyjątku, a drugi dla ogólnego bloku wyjątków.

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

Powyższy program generuje następujące dane wyjściowe.

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

Z powyższego wyniku widać wyraźnie, że nasz wyjątek został przechwycony przez blok przechwytywania „FileNotFoundException”, a nie ogólny.

Wreszcie Block

Ostatni blok następuje po bloku try lub bloku catch. Ostateczny blok kodu zawsze jest wykonywany, niezależnie od wystąpienia wyjątku.

Korzystanie z bloku last umożliwia uruchamianie dowolnych instrukcji typu czyszczenia, które chcesz wykonać, bez względu na to, co dzieje się w chronionym kodzie. Poniżej znajduje się składnia tego bloku.

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

Zmodyfikujmy powyższy kod i dodajmy ostatni blok kodu. Poniżej znajduje się fragment kodu.

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

Powyższy program generuje następujące dane wyjściowe.

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

Z powyższego programu widać, że ostatni blok jest również zaimplementowany po tym, jak blok catch przechwyci wymagany wyjątek.

Ponieważ Clojure wywodzi obsługę wyjątków z języka Java, podobnie jak w Javie, w Clojure dostępne są następujące metody do zarządzania wyjątkami.

  • public String getMessage()- Zwraca szczegółowy komunikat o wystąpieniu wyjątku. Ta wiadomość jest inicjowana w konstruktorze Throwable.

  • public Throwable getCause() - Zwraca przyczynę wyjątku reprezentowaną przez obiekt Throwable.

  • public String toString() - Zwraca nazwę klasy połączoną z wynikiem getMessage ().

  • public void printStackTrace() - Wyświetla wynik metody toString () wraz ze śladem stosu do System.err, strumienia wyjściowego błędu.

  • public StackTraceElement [] getStackTrace()- Zwraca tablicę zawierającą każdy element ze śladu stosu. Element pod indeksem 0 reprezentuje górę stosu wywołań, a ostatni element w tablicy reprezentuje metodę na dole stosu wywołań.

  • public Throwable fillInStackTrace() - Wypełnia ślad stosu tego obiektu Throwable bieżącym śladem stosu, dodając do wszelkich poprzednich informacji w śladzie stosu.

Poniżej znajduje się przykładowy kod wykorzystujący niektóre z metod wymienionych powyżej.

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

Powyższy program generuje następujące dane wyjściowe.

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