Clojure-exception-handling

提供:Dev Guides
移動先:案内検索

Clojure-例外処理

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

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

  • チェック済み例外-RuntimeExceptionおよびErrorを除くThrowableクラスを拡張するクラスは、チェック済み例外と呼ばれます。 E.g. 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を発生させたことが明確にわかります。

  • 未チェックの例外-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の例外

例外をキャッチする

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

try-catchブロックの一般的な構文は次のとおりです。

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

例外を発生させる可能性のあるすべてのコードは、*保護されたコードブロック*に配置されます。

  • catchブロック*で、アプリケーションが例外から回復できるように、カスタムコードを記述して例外を処理できます。

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

(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ブロック*に障害のあるコードをラップします。 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」キャッチブロックによって例外がキャッチされたことが明確にわかります。

最後にブロック

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

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

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

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

(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ブロックが必要な例外をキャッチした後に、最終ブロックも実装されていることがわかります。

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