2013-12-17 28 views
2

如何使用新的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(); 
    } 
} 

斯卡拉版本应该是这样的......

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

......但我要如何实现Java的finally等同?

+0

[数据压缩的可能重复在斯卡拉](http://stackoverflow.com/questions/20614343/data-compression-in-scala) –

+0

你应该使用scala-arm。 http://stackoverflow.com/questions/5149009/functional-try-catch-w-scala – tmbo

回答

3

由于尝试{}块绝不会抛出异常,也没有必要为一个finally语句。另外,对于这个特定的场景,你可能应该使用scala-arm,就像其他海报所建议的一样。

但是,您可以轻松地将最后一种方法添加到试图在成功或失败时执行副作用。

事情是这样的:

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

注意,在尝试所有方法的精神,这不会,如果你的行动将引发非致命异常抛出一个异常,而是简单地捕获它作为一个失败。

你会使用这样的:

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

注意,你没有在最后检查null,如果由于某种原因,深不可测的OutputStream中为空,你只会得到一个失败,因为(空指针异常)。另外,如果close抛出一个IOException,那么你只会得到一个Failure(IOException)。

+0

非常有趣...但我不会在非致命的情况下返回失败,因为如果由于任何原因outputStream.close()失败,这将防止deflate()返回压缩输出... Finally块旨在关闭输出流,不管Try块是否成功。 – j3d

+0

如果关闭ByteArrayOutputStream失败,则会出现严重错误,并且不返回压缩输出是您最担心的问题。为什么创建或关闭像ByteArrayOutputStream这样的东西会失败的唯一原因是OutOfMemoryError,这当然是致命的,无论如何都需要抛出。 –

5

斯卡拉的Try API不应该是直接相当于try-catch-finally在Java中构造。确实,为什么它应该是? Scala也内置了try-catch-finally结构。您可以直接在Java中使用它。当需要合并多个可能失败的操作时需要Try

实际上,你提出更复杂的问题 - 资源管理。你的代码,其实应该是这个样子的:

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

在Java中这种“尝试与 - 资源”的语言功能将自动关闭您在括号中指定try毕竟资源。

不幸的是,Scala没有在图书馆或在语言但其直接等同。但是因为Scala更具表现力,您可以手动编写此构造或使用第三方库,其中我建议使用scala-arm。请参见以下链接了解更多信息:

+0

是的,它看起来像scala-arm提供了我正在寻找的功能......我希望它会集成到官方的Scala发行。 – j3d

0

Choppy's Lazy TryClose monad是为这种场景设计的,您希望尝试使用资源。另外,它很懒,所以你可以编写东西。

这里是你如何使用它的一个例子:

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 
} 

此处了解详情:https://github.com/choppythelumberjack/tryclose

(只是一定要导入tryclose._tryclose.JavaImplicits._