在finally块中使用变量

时间:2013-07-13 06:25:14

标签: scala

以下是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。

我的问题是,我如何实现这一目标?

4 个答案:

答案 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相同。但是在斯卡拉你不必处理那些无用的东西。