以下是Scala中的代码:
def write() = {
try {
val out = new PrintWriter(new BufferedWriter(new FileWriter(fileName, true)))
out.println("123")
out.close
} catch {
case e: IOException => {}
}
//finally {
//out.close // ops, it's not visible in this context
//}
}
最好在out.close
块中使用“finally
”,不是吗?但我不想使用var。
我的问题是,我如何实现这一目标?
答案 0 :(得分:9)
块中定义的变量是该块的本地变量。因此,如果您坚持手动使用try / finally,则必须将val移出块。
但是,您要尝试实现的是创建资源,在块中使用它,并在离开块时调用它的close方法,无论您是通过异常正常还是异常地离开块。这是一个非常常见的问题,因此已经存在一个名为Scala ARM的库。 ARM代表自动资源管理。
以下是基本用法:
import resource._
for(input <- managed(new FileInputStream("test.txt")) {
// Code that uses the input as a FileInputStream
}
有人谈到将此构造移动到scala标准库,因此将来您可能甚至不需要外部依赖。
我建议使用类似这样的库。它只是build.sbt中的一行。但是出于教育目的,这就是你自己的方式:
def managed[T <: AutoCloseable](resource:T) = new Traversable[T] {
def foreach[U](f:T=>U) {
try {
f(resource)
} finally {
resource.close()
}
}
}
以下是如何使用它
scala> for(reader<-managed(new java.io.FileReader("/etc/passwd"))) { println(reader.read()) }
114
scala> for(reader<-managed(new java.io.FileReader("/etc/shadow"))) { println(reader.read()) }
java.io.FileNotFoundException: /etc/shadow (Permission denied)
...
您仍会获得异常,但会调用close。当然,如果close也会引发异常,那么这将隐藏原始异常。像这样的小细节可能在scala ARM中处理得更好。
答案 1 :(得分:3)
这是我用来管理传递给函数返回而不是重新调整期货的可关闭资源
def withClosable[ T, C <: Closeable ]( closable: C )( f: C ⇒ T ) = try { f( closable ) } finally { IOUtils closeQuietly closable }
def withFutureClosable[ T <: Future[Any], C <: Closeable ]( closable: C )( f: C ⇒ T ) = f( closable ) andThen {
case _ => IOUtils closeQuietly closable
}
}
我使用IOUtils
中的commons-io
来简化实际关闭资源的调用。一个简单的try { closable.close() } catch { case _ => /* blah */ }
会做
使用示例:
withClosable(new FileInpustream("f")) { stream => /* read the stream */ }
答案 2 :(得分:1)
贷款模式对于此用例更为常见,但由于Stack Overflow上有任何内容,您可以使用Try
构建您正在寻找的表达式。
Try
值得更多曝光作为一种便利工具。
scala> import util._
import util._
scala> import io._
import io._
尝试打开文件 -
scala> def f =
| Try (Source.fromFile("foo.text")) map { in =>
然后用它做一些事情,将结果打包到带有i / o源的元组中 - 请注意,当您在for-comprehension中执行值定义时,这就是它的作用 -
| (in, Try(in.getLines.mkString("/")))
| } flatMap {
然后关闭源并产生计算结果 -
| case (in, res) =>
| in.close()
| res
| }
f: scala.util.Try[String]
未注释:
scala> def f =
| Try (Source.fromFile("foo.text")) map { in =>
| (in, Try(in.getLines.mkString("/")))
| } flatMap {
| case (in, res) =>
| in.close()
| res
| }
f: scala.util.Try[String]
scala> f
res1: scala.util.Try[String] = Failure(java.io.FileNotFoundException: foo.text (No such file or directory))
使用一些经典的幽默文本创建测试文件,然后再试一次:
scala> f
res2: scala.util.Try[String] = Success(Now is the time/for all good dogs/to lie.)
你可以把它作为一种理解,虽然观察额外的展平,因为你从收益率得到一张地图而不是flatMap:
scala> def g = (for {
| in <- Try (Source.fromFile("foo.text"))
| res = Try(in.getLines.mkString("/"))
| } yield {
| in.close()
| res
| }).flatten
g: scala.util.Try[String]
scala> g
res2: scala.util.Try[String] = Success(Now is the time/for all good dogs/to lie.)
如果关闭失败,我们想要失败怎么办?
我不想再将所有内容输入REPL了!
scala> :hi // :history
[snip]
2490 def g = (for {
2491 in <- Try (Source.fromFile("foo.text"))
2492 res = Try(in.getLines.mkString("/"))
2493 } yield {
2494 in.close()
2495 res
2496 }).flatten
2497 :hi
scala> :edit 2490+7 // or just :edit 2490-
+import util._
+import io._
+def g = (for {
+ in <- Try (Source.fromFile("foo.text"))
+ res = Try(in.getLines.mkString("/"))
+} yield {
+ val ok = Try(in.close())
+ res transform (s => ok map (_ => s), new Failure(_))
+}).flatten
+
import util._
import io._
g: scala.util.Try[String]
transform
表示如果计算成功,如果结果ok
的结果失败,则将该成功转换为失败;并且在计算失败的情况下,保持这种失败,尽管有些人更愿意加错。
难道你不知道try / catch是如此。 &LT; /滑稽&GT; (滑稽并不意味着巨魔。)
答案 3 :(得分:1)
或者只是:
val is = new FileInputStream(file)
val result = try {
// do stuff
} finally {
is.close()
}
因为is
无法为空。
在您的问题代码中,只需将val out
移到try block之外。这样,除了null case之外,它将与Java AutoCloseable相同。但是在斯卡拉你不必处理那些无用的东西。