Clojure-例外処理

Exception handlingアプリケーションの通常のフローを維持できるように、ランタイムエラーを処理するためにプログラミング言語で必要です。例外は通常、アプリケーションの通常のフローを中断します。これが、アプリケーションで例外処理を使用する必要がある理由です。

例外は大きく次のカテゴリに分類されます-

  • Checked Exception− RuntimeExceptionとErrorを除いて、Throwableクラスを拡張するクラスは、チェック例外と呼ばれます。例:IOException、SQLExceptionなど。チェックされた例外はコンパイル時にチェックされます。

Example.txtというファイルに対して操作を行う次のプログラムについて考えてみましょう。ただし、Example.txtファイルが存在しない場合が常にあります。

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

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

Example.txtファイルが存在しない場合、プログラムによって次の例外が生成されます。

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)

上記の例外から、プログラムがFileNotFoundExceptionを発生させたことがはっきりとわかります。

  • Unchecked Exception− RuntimeExceptionを拡張するクラスは、未チェックの例外と呼ばれます。たとえば、ArithmeticException、NullPointerException、ArrayIndexOutOfBoundsExceptionなどです。チェックされていない例外は、コンパイル時にチェックされるのではなく、実行時にチェックされます。

古典的なケースの1つは、配列の長さよりも大きい配列のインデックスにアクセスしようとしたときに発生するArrayIndexOutOfBoundsExceptionです。以下は、この種の間違いの典型的な例です。

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

上記のコードを実行すると、次の例外が発生します。

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

エラー

エラーは回復不能です(例:OutOfMemoryError、VirtualMachineError、AssertionErrorなど)。これらはプログラムが回復できないエラーであり、プログラムをクラッシュさせます。これらの例外が存在する場合でもプログラムを実行し続けることができるように、これらの例外をキャッチするためのメカニズムが必要です。

次の図は、Clojureの例外の階層がどのように編成されているかを示しています。これはすべて、Javaで定義された階層に基づいています。

例外をキャッチする

他のプログラミング言語と同様に、Clojureは通常の「try-catch」ブロックを提供して、例外が発生したときに例外をキャッチします。

以下は、try-catchブロックの一般的な構文です。

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

例外を発生させる可能性のあるすべてのコードは、 Protected code block

の中に catch block、アプリケーションが例外から回復できるように、例外を処理するカスタムコードを記述できます。

file-not-found例外を生成した以前の例を見て、trycatchブロックを使用してプログラムによって発生した例外をキャッチする方法を見てみましょう。

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

上記のプログラムは次の出力を生成します。

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

上記のコードから、障害のあるコードを try block。catchブロックでは、例外をキャッチして、例外が発生したというメッセージを出力しています。これで、プログラムによって生成された例外をキャプチャする意味のある方法ができました。

複数のキャッチブロック

複数のタイプの例外を処理するために、複数のcatchブロックを持つことができます。発生した例外のタイプに応じて、catchブロックごとに、それに応じて処理するコードを記述します。

以前のコードを変更して、2つのcatchブロックを含めましょう。1つはファイルが見つからない例外に固有で、もう1つは一般的な例外ブロック用です。

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

上記のプログラムは次の出力を生成します。

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

上記の出力から、例外が一般的なものではなく、「FileNotFoundException」キャッチブロックによってキャッチされたことがはっきりとわかります。

最後にブロック

finalブロックは、tryブロックまたはcatchブロックの後に続きます。例外の発生に関係なく、コードのfinallyブロックは常に実行されます。

finishブロックを使用すると、保護されたコードで何が起こっても、実行したいクリーンアップタイプのステートメントを実行できます。このブロックの構文は次のとおりです。

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

上記のコードを変更して、finallyブロックのコードを追加しましょう。以下はコードスニペットです。

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

上記のプログラムは次の出力を生成します。

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

上記のプログラムから、catchブロックが必要な例外をキャッチした後、finalブロックも実装されていることがわかります。

ClojureはJavaと同様にJavaから例外処理を派生させるため、Clojureでは例外を管理するために次のメソッドを使用できます。

  • public String getMessage()−発生した例外に関する詳細メッセージを返します。このメッセージは、Throwableコンストラクターで初期化されます。

  • public Throwable getCause() −Throwableオブジェクトで表される例外の原因を返します。

  • public String toString() − getMessage()の結果と連結されたクラスの名前を返します。

  • public void printStackTrace() − toString()の結果をスタックトレースとともにエラー出力ストリームであるSystem.errに出力します。

  • public StackTraceElement [] getStackTrace()−スタックトレース上の各要素を含む配列を返します。インデックス0の要素は呼び出しスタックの最上位を表し、配列の最後の要素は呼び出しスタックの最下部のメソッドを表します。

  • public Throwable fillInStackTrace() −このThrowableオブジェクトのスタックトレースを現在のスタックトレースで埋め、スタックトレースの以前の情報に追加します。

以下は、上記のメソッドのいくつかを使用するサンプルコードです。

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

上記のプログラムは次の出力を生成します。

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