在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()
}
答案 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中,使用Try
,Either
或我们自己的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)
}