kmizuの日記

プログラミングや形式言語に関係のあることを書いたり書かなかったり。

IOと例外の取り扱いについて:どっちが良いスタイル?

追記:Java 7以降(つまり、現在)はtry-with-resources構文があるので、それを使えばよいです。ここでは、Java 6かそれ以前のコーディングスタイルについて主に言っています。

kmizu.hatenablog.com

におけるkrxrossさんのコメント

java脳だと、「fw = new PrintWriter(new File(file))」で例外が発生したら、fwが不定のまま、finally句の「fw != null」で困ってしまう。 そうならないために、nullで初期化するという話に見えます。 多分JAVAだと、nullを代入しないとコンパイルエラーになるか、eclipsが警告を出したと思います。 scalaでも同じではないですか ? 良く解っていない超初心者より

を見て、強烈に不安になってしまったのですが、Javaだとこういうのが良いスタイルなんでしたっけ?

//Style (A)
package ex.callbyname
import java.io.File
import java.io.FileWriter
def fwloop (file: String, cond: => Boolean, update: => Unit)(body: => String) {
  var fw: FileWriter = null
  try {
    fw = new PrintWriter(new File(file))
    while (cond) {
      fw.write(body)
      update
    }
  } catch {
    case e: Exception => println("Error: " + e.getMessage())
  } finally {
    if (fw != null) {
      fw.close()
    }
  }
}

いや、このスタイル、Javaで正直結構見たことあるのですが、ファイルオープンの失敗と、ファイル書き込み中の失敗という意味の異なる例外を同じ階層で扱うという点であまりよくないというか、ファイルオープンの失敗はリカバー可能な場合が多いが、ファイル書き込み中の例外は外に投げっぱなしにすることしかできないことが多いという点で非常によくないスタイルだと思うのですがいかがでしょうか?

自分なら、このコードは、

//Style (B)
package ex.callbyname
import java.io.File
import java.io.FileWriter
def fwloop (file: String, cond: => Boolean, update: => Unit)(body: => String) {
  try {
    val fw = new PrintWriter(new File(file))
    try {
      while (cond) {
        fw.write(body)
        update
      }
    } catch {
      case e: Exception => println("Error In IO: " + e.getMessage())
    } finally {
      fw.close()
    }
  } catch {
    case e: Exception => println("Error in Open: " + e.getMesssage())
  }
}

のように書きます。この方が、異なる階層の例外を別々に処理できるうえに、ブロック付きopenのようなものを書きたい場合にもスタイルを変えずに済むからです:

import java.io.File
import java.io.FileWriter
def openForWrite(file: String)(body: PrintWriter => A): A = {
  val fw = new PrintWriter(new File(file))
  try {
    body(fw)
  } finally {
    fw.close()
  }
}

皆さんはどう思われるでしょうか?