简单的Scala模式用于"使用/ try-with-resources" (自动资源管理)

时间:2014-09-03 00:33:58

标签: scala resource-management

C#的using接口为IDisposable。 Java 7+具有与tryAutoCloseable接口相同的功能。 Scala允许您为此问题选择自己的实现。

scala-arm似乎是受欢迎的选择,由一名Typesafe员工维护。但是,这种简单的行为似乎非常复杂。为了澄清,使用说明很简单,但了解所有代码在内部工作的方式相当复杂。

我刚刚编写了以下超级简单的ARM解决方案:

object SimpleARM {
  def apply[T, Q](c: T {def close(): Unit})(f: (T) => Q): Q = {
    try {
      f(c)
    } finally {
      c.close()
    }
  }
}
  • 简单臂之类的东西有什么好处吗?似乎所有额外的复杂性都应该带来额外的好处。
  • 通常情况下,最好使用其他人支持的公共开源库,以便使用自定义代码来实现通用行为。
  • 有人可以推荐任何改进吗?
  • 这种简单方法有任何限制吗?

8 个答案:

答案 0 :(得分:11)

只要您不需要使用多个资源,只需要管理一个简单的贷款模式,您的方法就可以正常运行。 scala-arm monadic方法允许这样做。

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},而不是真正复杂的btw。

答案 1 :(得分:4)

这是我的新手简单,一目了然,Scala ARM。这完全支持我能想到的每个用例,包括多个资源和产量值。这使用了非常简单的理解用法语法:

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")
  }
}

答案 2 :(得分:3)

这是我使用的代码:

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()方法。 它只支持一种资源。

以下是使用InputStream的示例:

val path = Paths get "/etc/myfile"
use(Files.newInputStream(path)) { inputStream ⇒
    val firstByte = inputStream.read()
    ....
}

答案 3 :(得分:1)

http://illegalexception.schlichtherle.de/2012/07/19/try-with-resources-for-scala/

另一个实现,从“遵循Java规范”的角度来看可能更干净,但也无法支持多个资源

答案 4 :(得分:1)

这个对我很有用:

none

在此Apache Cassandra客户端代码中使用它:

  implicit class ManagedCloseable[C <: AutoCloseable](resource: C) {
    def apply[T](block: (C) => T): T = {
    try {
      block(resource)
    } finally {
      resource.close()
    }
  }

甚至更短:

val metadata = Cluster.builder().addContactPoint("vader").withPort(1234).build() { cluster =>
  cluster.getMetadata
}

答案 5 :(得分:1)

Choppy的Lazy TryClose monad可能就是你想要的。它与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

答案 6 :(得分:0)

我可以向您建议的方法推荐一项改进,即:

  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
  }

IMHO拥有Try[B]的tryResult,可以让你以后更容易控制流程。

答案 7 :(得分:0)

我会这样做:

  def tryFinally[A, B](acquire: Try[A])(use: A => B)(release: A => Unit): Try[B] =
    for {
      resource <- acquire
      r = Try(use(resource)).fold(
        e => { release(resource); throw e },
        b => { release(resource); b }
      )
    } yield r

您可以对其调用 .get 并使其返回 B