简体   繁体   English

我如何表达*最终*等同于Scala的尝试?

[英]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? 如何使用新的Try API将以下Java代码转换为Scala?

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... Scala版本应该是这样的......

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? ...但是我如何实现Java的finally等价?

Scala's Try API is not supposed to be direct equivalent to try-catch-finally construct in Java. Scala的Try API不应该直接等同于Java中的try-catch-finally构造。 Indeed, why it should be? 的确,它应该是什么原因? Scala has built-in try-catch-finally construct too. Scala也内置了try-catch-finally构造。 You can use it directly as in Java. 您可以像在Java中一样直接使用它。 Try is needed when you need to combine multiple actions which can fail. 当您需要组合可能失败的多个操作时,需要Try

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 . Java中的“try-with-resources”语言功能会在try后自动关闭您在括号中指定的所有资源。

Unfortunately, Scala does not have its direct equivalent in the library or in the language yet. 不幸的是,Scala在库或语言中没有它的直接等价物。 But because Scala is much more expressive, you can write this construct manually or use third-party libraries, among which I recommend scala-arm . 但是因为Scala更具表现力,你可以手动编写这个结构或使用第三方库,其中我推荐使用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. 由于Try {}块永远不会抛出异常,因此不需要finally语句。 Also, for this particular scenario you should probably use scala-arm, like other posters have suggested. 此外,对于这种特殊情况,您可能应该使用scala-arm,就像其他海报所建议的那样。

But you can easily add a finally method to a Try that performs a side-effect in case of either success or failure. 但是,您可以轻松地将一个finally方法添加到Try中,以便在成功或失败的情况下执行副作用。

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. 请注意,在Try的所有方法的精神中,如果您的操作抛出非致命异常,但只是将其捕获为失败,则不会抛出异常。

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). 请注意,您不必在Finally中检查null,因为如果由于某些不可思议的原因,outputStream为null,您将只获得一个Failure(NullPointerException)。 Also, in the event that close throws an IOException you will just get a Failure(IOException). 此外,如果close抛出IOException,您将收到Failure(IOException)。

Choppy's Lazy TryClose monad is made for this kind of scenario where you want try-with-resources. Choppy的Lazy TryClose monad适用于这种需要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 更多信息: https//github.com/choppythelumberjack/tryclose

(just be sure to import tryclose._ and tryclose.JavaImplicits._ ) (只需确保导入tryclose._tryclose.JavaImplicits._

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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