Scala最终阻止关闭/刷新资源

Dzh*_*zhu 21 scala

有没有更好的方法来确保资源得到正确发布 - 更好的方法来编写以下代码?

        val out: Option[FileOutputStream] = try {
          Option(new FileOutputStream(path))
        } catch {
          case _ => None
        }


        if (out.isDefined) {

          try {
            Iterator.continually(in.read).takeWhile(-1 != _).foreach(out.get.write)
          } catch {
            case e => println(e.getMessage)
          } finally {
            in.close
            out.get.flush()
            out.get.close()
          }

        }
Run Code Online (Sandbox Code Playgroud)

Rex*_*err 20

这样的东西是个好主意,但我会把它变成一个方法:

def cleanly[A,B](resource: => A)(cleanup: A => Unit)(code: A => B): Option[B] = {
  try {
    val r = resource
    try { Some(code(r)) }
    finally { cleanup(r) }
  } catch {
    case e: Exception => None
  }
}
Run Code Online (Sandbox Code Playgroud)

(注意我们只捕获一次;如果你真的想要在一个案例中打印一条消息而不是另一个案例,那么你必须像你一样抓住这两个消息).(另请注意,我只捕获异常;捕获Error也通常是不明智的,因为它几乎不可能从中恢复.)方法的使用方式如下:

cleanly(new FileOutputStream(path))(_.close){ fos =>
  Iterator.continually(in.read).takeWhile(_ != -1).foreach(fos.write)
}
Run Code Online (Sandbox Code Playgroud)

因为它返回一个值,所以Some(())如果它在这里成功了(你可以忽略它).


编辑:为了使它更通用,我真的让它返回一个Either,所以你得到了例外.像这样:

def cleanly[A,B](resource: => A)(cleanup: A => Unit)(code: A => B): Either[Exception,B] = {
  try {
    val r = resource
    try { Right(code(r)) } finally { cleanup(r) }
  }
  catch { case e: Exception => Left(e) }
}
Run Code Online (Sandbox Code Playgroud)

现在,如果你得到了Right,一切都没问题.如果你得到了Left,你可以选择你的例外.如果你不关心异常,你可以使用.right.toOption它将它映射到一个选项,或者只是使用.right.map或者用什么来操作正确的结果只有它存在(就像Option).(模式匹配是处理Eithers 的有用方法.)

  • @rvange - 生成资源可能会导致异常,因此您希望按名称进行调用.资源可能不是`java.io.Closeable`,因此允许用户指定的清理更通用.如果您只有`java.io.Closeable`s并且您确定该资源将毫无例外地创建自己,或者您希望该异常传播,那么您的代码就可以了. (3认同)

Der*_*att 17

看看Scala-ARM

该项目旨在成为scala库中自动资源管理的Scala Incubator项目......

... Scala ARM库允许用户使用"托管"方法确保在代码块内打开资源."托管"方法本质上采用"具有close或dispose方法的任何东西"的参数,并构造一个新的ManagedResource对象.