简体   繁体   中英

How can I express *finally* equivalent for a Scala's Try?

How do I translate the following Java code to Scala using the new Try API?

public byte[] deflate(byte[] data) {

    ByteArrayOutputStream outputStream = null;
    GZIPOutputStream gzipOutputStream = null;

    try {
        outputStream = new ByteArrayOutputStream();
        gzipOutputStream = new GZIPOutputStream(outputStream);
        gzipOutputStream.write(data);
        return outputStream.toByteArray();
    catch (Exception e) {
        ...
    } finally {
        if (gzipOutputStream != null) gzipOutputStream.close();
    }
}

The Scala version should be something like this...

def deflate(data Array[Byte]): Try[Array[Byte]] = Try {
  ByteArrayOutputStream outputStream = new ByteArrayOutputStream()
  new GZIPOutputStream(outputStream).write(data)
  outputStream.toByteArray
}

... but how do I implement Java's finally equivalent?

Scala's Try API is not supposed to be direct equivalent to try-catch-finally construct in Java. Indeed, why it should be? Scala has built-in try-catch-finally construct too. You can use it directly as in Java. Try is needed when you need to combine multiple actions which can fail.

In fact, you raise more complex problem - resource management. You code, in fact, should have looked like this:

try (ByteArrayOutputStream os = new ByteArrayOutputStream(data);
     GZIPOutputStream gzos = new GZIPOutputStream(os)) {
    gzipOutputStream.write(data);
    return outputStream.toByteArray();
} catch (Exception e) {
    ...
}

This "try-with-resources" language feature in Java automatically closes all resources you specify in parentheses after try .

Unfortunately, Scala does not have its direct equivalent in the library or in the language yet. But because Scala is much more expressive, you can write this construct manually or use third-party libraries, among which I recommend scala-arm . See these links for more information:

Since a Try {} block will never throw an exception, there is no need for a finally statement. Also, for this particular scenario you should probably use scala-arm, like other posters have suggested.

But you can easily add a finally method to a Try that performs a side-effect in case of either success or failure.

Something like this:

implicit class TryHasFinally[T](val value:Try[T]) extends AnyVal { 
  import scala.util.control.NonFatal

  def Finally(action: => Unit) : Try[T] = 
    try { 
      action; 
      value 
    } catch { 
      case NonFatal(cause) => Failure[T](cause)
    } 
}

Note that in the spirit of all methods of Try, this will not throw an exception if your action throws a non-fatal exception, but simply capture it as a Failure.

You would use it like this:

import java.io._
import java.util.zip._

def deflate(data: Array[Byte]): Try[Array[Byte]] = {
  var outputStream : ByteArrayOutputStream = null
  Try {
    outputStream = new ByteArrayOutputStream()
    new GZIPOutputStream(outputStream).write(data)
    outputStream.toByteArray
  } Finally {
    outputStream.close()
  }
}

Note that you don't have to check for null in the Finally, since if for some unfathomable reason the outputStream is null you will just get a Failure(NullPointerException). Also, in the event that close throws an IOException you will just get a Failure(IOException).

Choppy's Lazy TryClose monad is made for this kind of scenario where you want try-with-resources. Plus, it's lazy so you can compose stuff.

Here is an example of how you would use it:

val output = for {
  outputStream      <- TryClose(new ByteArrayOutputStream())
  gzipOutputStream  <- TryClose(new GZIPOutputStream(outputStream))
  _                 <- TryClose.wrap(gzipOutputStream.write(data))
} yield wrap(outputStream.toByteArray())

// Does not actually run anything until you do this:
output.resolve.unwrap match {
    case Success(bytes) => // do something with bytes
    case Failure(e) => // handle exception
}

More info here: https://github.com/choppythelumberjack/tryclose

(just be sure to import tryclose._ and tryclose.JavaImplicits._ )

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM