2

这是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.closefinally块中有“”,不是吗?但我不想使用 var。

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

4

4 回答 4

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 中处理得更好。

于 2013-07-13T06:47:27.327 回答
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
    } 
}

我使用IOUtilsfromcommons-io来简化实际关闭资源的调用。一个简单的try { closable.close() } catch { case _ => /* blah */ }就行

示例用法:

withClosable(new FileInpustream("f")) { stream => /* read the stream */ }

于 2013-07-13T08:22:32.683 回答
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 是 1990 年代。</droll>(droll 并不意味着巨魔。)

于 2013-07-13T08:20:09.953 回答
1

要不就:

val is = new FileInputStream(file)
val result = try {
    // do stuff
} finally {
    is.close()
}

因为没有办法is可以为空。

在您的问题代码中,只需移出val outtry 块。这样,它将与 Java AutoCloseable 所做的相同,但 null 情况除外。但在 Scala 中,您不必处理可空值。

于 2015-01-01T09:08:11.630 回答