C#にはusing
インターフェイスを備えたIDisposable
があります。 Java 7+はtry
およびAutoCloseable
インターフェースと同じ機能を備えています。Scalaこれにより、独自の実装を選択できます問題。
scala-armは人気のある選択肢のようで、Typesafeの従業員の1人によって保守されています。ただし、このような単純な動作では非常に複雑に見えます。明確にするために、使用手順は単純ですが、そのすべてのコードが内部でどのように機能しているかを理解することはかなり複雑です。
私はちょうど次の超単純なARMソリューションを書きました:
object SimpleARM {
def apply[T, Q](c: T {def close(): Unit})(f: (T) => Q): Q = {
try {
f(c)
} finally {
c.close()
}
}
}
これが私の新しいシンプルで一目でわかるScala ARMです。これは、複数のリソースとyield値を含めることを考えることができるすべてのユースケースを完全にサポートします。これは、理解のための非常にシンプルな使用構文を使用します。
class AutoCloseableWrapper[A <: AutoCloseable](protected val c: A) {
def map[B](f: (A) => B): B = {
try {
f(c)
} finally {
c.close()
}
}
def foreach(f: (A) => Unit): Unit = map(f)
// Not a proper flatMap.
def flatMap[B](f: (A) => B): B = map(f)
// Hack :)
def withFilter(f: (A) => Boolean) = this
}
object Arm {
def apply[A <: AutoCloseable](c: A) = new AutoCloseableWrapper(c)
}
デモの使用法は次のとおりです。
class DemoCloseable(val s: String) extends AutoCloseable {
var closed = false
println(s"DemoCloseable create ${s}")
override def close(): Unit = {
println(s"DemoCloseable close ${s} previously closed=${closed}")
closed = true
}
}
object DemoCloseable {
def unapply(dc: DemoCloseable): Option[(String)] = Some(dc.s)
}
object Demo {
def main(args: Array[String]): Unit = {
for (v <- Arm(new DemoCloseable("abc"))) {
println(s"Using closeable ${v.s}")
}
for (a <- Arm(new DemoCloseable("a123"));
b <- Arm(new DemoCloseable("b123"));
c <- Arm(new DemoCloseable("c123"))) {
println(s"Using multiple resources for comprehension. a.s=${a.s}. b.s=${b.s}. c.s=${c.s}")
}
val yieldInt = for (v <- Arm(new DemoCloseable("abc"))) yield 123
println(s"yieldInt = $yieldInt")
val yieldString = for (DemoCloseable(s) <- Arm(new DemoCloseable("abc")); c <- s) yield c
println(s"yieldString = $yieldString")
println("done")
}
}
複数のリソースを操作する必要がなく、すべてを管理する必要がある限り、単一の単純なローンパターンを使用したアプローチは問題なく機能します。これは、scala-armモナドアプローチで可能です。
import resource.managed
managed(openResA).and(managed(openResB)) acquireFor { (a, b) => ??? }
val res = for {
a <- managed(openResA)
b <- managed(openResB)
c <- managed(openResC)
} yield (a, b, c)
res acquireAndGet {
case (a, b, c) => ???
}
Scala-armで知っておくべき主な関数はresource.managed
および.acquired{For,AndGet}
、それほど複雑ではありません。
これは私が使用するコードです:
_def use[A <: { def close(): Unit }, B](resource: A)(code: A ⇒ B): B =
try
code(resource)
finally
resource.close()
_
Java try-with-resourcesとは異なり、リソースは実装する必要はありません AutoCloseable 。close()
メソッドのみが必要です。サポートするだけです。 1つのリソース。
InputStream
での使用例を次に示します。
_val path = Paths get "/etc/myfile"
use(Files.newInputStream(path)) { inputStream ⇒
val firstByte = inputStream.read()
....
}
_
http://illegalexception.schlichtherle.de/2012/07/19/try-with-resources-for-scala/
別の実装。おそらく「follow Java仕様」の観点からはよりクリーンですが、複数のリソースをサポートできません。
これは私にとって本当にうまくいきます:
implicit class ManagedCloseable[C <: AutoCloseable](resource: C) {
def apply[T](block: (C) => T): T = {
try {
block(resource)
} finally {
resource.close()
}
}
たとえば、このApacheで使用するCassandraクライアントコード:
val metadata = Cluster.builder().addContactPoint("vader").withPort(1234).build() { cluster =>
cluster.getMetadata
}
またはさらに短い:
val metadata = Cluster.builder().addContactPoint("sedev01").withPort(9999).build()(_.getMetadata)
あなたが提案したアプローチに対して私が推奨できる改善点は次のとおりです。
def autoClose[A <: AutoCloseable, B](resource: A)(code: A ⇒ B): B = {
try
code(resource)
finally
resource.close()
}
使用すること:
def autoClose[A <: AutoCloseable, B](resource: A)(code: A ⇒ B): Try[B] = {
val tryResult = Try {code(resource)}
resource.close()
tryResult
}
Try[B]
であるtryResultを持つIMHOを使用すると、後で制御フローを簡単に制御できます。
ChoppyのLazyTryCloseモナドはあなたが探しているものかもしれません(開示:私は著者です)。これはScalaのTryと非常に似ていますが、リソースを自動的に閉じます。
val ds = new JdbcDataSource()
val output = for {
conn <- TryClose(ds.getConnection())
ps <- TryClose(conn.prepareStatement("select * from MyTable"))
rs <- TryClose.wrap(ps.executeQuery())
} yield wrap(extractResult(rs))
// Note that Nothing will actually be done until 'resolve' is called
output.resolve match {
case Success(result) => // Do something
case Failure(e) => // Handle Stuff
}
詳細については、こちらをご覧ください: https://github.com/choppythelumberjack/tryclose