是否有更好的方法来确保资源得到正确发布 - 更好的方法来编写以下代码?
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()
}
}
答案 0 :(得分: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
}
}
(请注意,我们只抓一次;如果你真的想要在一个案例中打印一条消息而不是另一个案例,那么你必须像你一样抓住这两个消息)。 (另请注意,我只捕获异常;捕获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
)。 (模式匹配是处理Either
的有用方法。)
答案 1 :(得分:17)
该项目旨在成为scala库中自动资源管理的Scala Incubator项目......
... Scala ARM库允许用户使用“托管”方法确保在代码块内打开资源。 “托管”方法本质上接受“具有close或dispose方法的任何东西”的参数,并构造一个新的ManagedResource对象。
答案 2 :(得分:0)
或者你也可以使用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._
)