【问题标题】:Scala finally block closing/flushing resourceScala 最终阻止关闭/刷新资源
【发布时间】:2012-02-10 13:15:53
【问题描述】:

有没有更好的方法来确保资源被正确释放 - 编写以下代码的更好方法?

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

        }

【问题讨论】:

  • 因为我需要能够嵌套多个 java.lang.AutoCloseable 实例,每个实例都依赖于前一个成功实例化的实例,所以我终于找到了一个对我非常有用的模式。我把它写成类似 StackOverflow 问题的答案:stackoverflow.com/a/34277491/501113

标签: scala


【解决方案1】:

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

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
  }
}

(请注意,我们只捕获一次;如果您真的想要在一种情况下打印消息而不是另一种情况,那么您必须像以前那样捕获两者)。 (另请注意,我只捕获异常;捕获Error 通常也是不明智的,因为几乎不可能从中恢复。)该方法的使用如下:

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

由于它返回一个值,如果它在这里成功,你会得到一个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) }
}

现在,如果您收到Right,一切顺利。如果您收到Left,您可以挑选出您的例外。如果您不关心异常,可以使用.right.toOption 将其映射到选项中,或者仅使用.right.map 或仅在正确结果存在时对正确结果进行操作(就像Option 一样) . (模式匹配是处理Eithers 的有用方法。)

【讨论】:

  • 为什么将资源分配给r?为什么不直接使用。你觉得这个简化有什么问题吗,去掉一次尝试。 def cleanly[A <: java.io.Closeable, B](resource: A)(code: A => B): Either[Exception, B] = { try { Right(code(resource)) } catch { case e:Exception => Left(e) } finally { resource.close } }
  • @rvnge - 生成资源可能会导致异常,因此您想按名称调用。该资源可能不是java.io.Closeable,因此允许用户指定的清理更为通用。如果您只有 java.io.Closeables 并且您确定资源将毫无例外地自行创建,或者您希望传播该异常,那么您的代码就可以了。
  • 很好,但是如果你将内部 try 块重构为另一种方法,您永远不会在外部尝试方法中知道这一点。我错过了什么吗?
  • C# 有类似 using(val resource = ...) {.. } 的东西,即使有异常,编译器也会在存在之前自动调用 dispose,希望 Scala 有类似的东西
  • @skjagini - 它在 2.13 中得到了一个(作为一种库方法,是对上述那种模式的仔细阐述)。
【解决方案2】:

看看Scala-ARM

该项目旨在成为 Scala 库中自动资源管理的 Scala 孵化器项目 ...

... Scala ARM 库允许用户使用“托管”方法确保打开关闭代码块内的资源。 “托管”方法本质上接受“任何具有关闭或处置方法的事物”的参数并构造一个新的 ManagedResource 对象。

【讨论】:

  • 你知道Scala-ARM的现状吗?它看起来已经死了——自 5 月以来没有提交。
【解决方案3】:

或者,您可以使用 Choppy 的 Lazy TryClose monad 来执行此操作。

val output = for {
  fin   <- TryClose(in)
  fout  <- TryClose.wrapWithCloser(new FileOutputStream(path))(out => {out.flush(); out.close();})
} yield wrap(Iterator.continually(fin.read).takeWhile(-1 != _).foreach(fout.get.write))

// Then execute it like this:
output.resolve

更多信息在这里:https://github.com/choppythelumberjack/tryclose

(一定要导入tryclose._tryclose.JavaImplicits._

【讨论】:

    猜你喜欢
    • 2021-05-25
    • 2019-06-17
    • 2017-07-16
    • 2016-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多