最常用的方法是在Scala中编写try / catch / finally?

时间:2016-12-12 23:49:10

标签: scala

在Scala中编写以下内容的最佳方法是什么?它对我来说看起来不太合适 - 首先是2个vals的前向声明,然后是长PrintWriter创建行,然后是finally块。唯一不恰当的是catch块......

val outputStream = Try(fs.create(tmpFile))
val writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(outputStream.get)))

if (outputStream.isFailure) {
  logger.error(s"Couldn't open file: $tmpFile")
}

try {

  features.foreach {
    case (sectionName, modelRDD) =>
      writer.append("{" + sectionName + ", " + modelRDD.getNumPartitions + "}")
  }

} catch {
  case e: Exception =>
    logger.error(s"Got exception", e)
    throw e

} finally {
  outputStream.get.close()
  writer.close()
}

2 个答案:

答案 0 :(得分:4)

我们可以进一步使用初始Try的上下文来执行完整的I / O操作:

首先,我们定义一个封装我们流程的函数:

def safeFilePrint(tf: => OutputStream)(op: PrintWriter => Unit): Try[Unit] = {
  val os = Try(tf) 
  val write = {
      val writer = os.map(f => new PrintWriter(f))
      val writeOp = writer.map(op)
      val flushOp = writer.map(_.flush)
      writeOp.flatMap(_ => flushOp)
  } 
  val close = os.map(_.close)
  write.flatMap(_ => close)
}

用法:

val collection = Seq(...)
val writeResult = safeFilePrint(new FileOutputStream(new File("/tmp/foo.txt"))){w => 
    collection.foreach(elem => w.write(e)
}

请注意,与原始代码相比,我们有写操作的结果。如果一切顺利或writeResult出现问题,Success(())将为Failure(exception)。基于此,我们的应用程序可以进一步决定做什么。

有人可能会疑惑:" finally在哪里?" 在Java中,finally用于确保某些代码(通常是资源管理)即使在try范围内抛出的异常会导致跟踪异常处理路径的情况下,也会被执行。

在Scala中,使用TryEither或我们自己的ADT等构造,我们将错误处理提升到应用程序级别。 finally变得不必要,因为我们的程序能够将失败作为程序的另一个有效状态来处理。

答案 1 :(得分:3)

在阅读@ maasg的答案之后,最终确定了该代码,该答案突出了monadic流程并且更“对称”。它看起来比OP中的代码好多了!

def safePrintToStream(gen: => OutputStream)(op: PrintWriter => Unit): Try[Unit] = {
  val os = Try(gen)
  val writer = os.map(stream => new PrintWriter(stream))
  val write = writer.map(op(_))
  val flush = writer.map(_.flush)
  val close = os.map(_.close)
  write.flatMap(_ => flush).flatMap(_ => close)
}