Simple Scala pattern for “using/try-with-resources” (Automatic Resource Management)

后端 未结 7 1794
别那么骄傲
别那么骄傲 2020-12-06 00:37

C# has using with the IDisposable interface. Java 7+ has identical functionality with try and the AutoCloseable interface

相关标签:
7条回答
  • 2020-12-06 01:11

    Here is my newer simple, understand at a glance, Scala ARM. This fully supports every use case I can think of including multiple resources and yield values. This uses a very simple for comprehension usage syntax:

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

    Here's demo use:

    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")
      }
    }
    
    0 讨论(0)
  • 2020-12-06 01:11

    This is the code I use:

    def use[A <: { def close(): Unit }, B](resource: A)(code: A ⇒ B): B =
        try
            code(resource)
        finally
            resource.close()
    

    Unlike Java try-with-resources, the resource doesn't need to implement AutoCloseable. Only a close() method is needed. It only supports one resource.

    Here is an example use with an InputStream:

    val path = Paths get "/etc/myfile"
    use(Files.newInputStream(path)) { inputStream ⇒
        val firstByte = inputStream.read()
        ....
    }
    
    0 讨论(0)
  • 2020-12-06 01:12

    Choppy's Lazy TryClose monad might be what you are looking for (disclosure: I'm the author). It is very similar to Scala's Try but automatically closes resources automatically.

    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
    }
    

    See here for more info: https://github.com/choppythelumberjack/tryclose

    0 讨论(0)
  • 2020-12-06 01:17

    this one works for me really well:

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

    using it for example in this Apache Cassandra client code:

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

    or even shorter:

    val metadata = Cluster.builder().addContactPoint("sedev01").withPort(9999).build()(_.getMetadata)
    
    0 讨论(0)
  • 2020-12-06 01:19

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

    Another implementation, probably more clean from "follow Java specifications" viewpoint, but also fails to support multiple resources

    0 讨论(0)
  • 2020-12-06 01:21

    An improvement I can recommend to the approach you suggested, which is:

      def autoClose[A <: AutoCloseable, B](resource: A)(code: A ⇒ B): B = {
        try
          code(resource)
        finally
          resource.close()
      }
    

    Is to use:

      def autoClose[A <: AutoCloseable, B](resource: A)(code: A ⇒ B): Try[B] = {
        val tryResult = Try {code(resource)}
        resource.close()
        tryResult
      }
    

    IMHO having the tryResult which is an Try[B], will allow you an easier control flow later.

    0 讨论(0)
提交回复
热议问题