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?
The Try type represents a computation that may either result in an exception, or return a successfully computed value. It's similar to, but semantically different from the scala. util.
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).
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:
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._
)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With