在try-with-resources块中管理多个链式资源的正确习惯用法? [英] Correct idiom for managing multiple chained resources in try-with-resources block?

查看:3755
本文介绍了在try-with-resources块中管理多个链式资源的正确习惯用法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Java 7 try-with-resources 语法(也称为ARM块(自动资源管理))在仅使用一个<$>时很简单,简单明了c $ c> AutoCloseable 资源。但是,当我需要声明多个依赖于彼此的资源时,我不确定什么是正确的习惯用法,例如 FileWriter 包装它的BufferedWriter 。当然,这个问题涉及一些 AutoCloseable 资源被包装的情况,而不仅仅是这两个特定的类。

The Java 7 try-with-resources syntax (also known as ARM block (Automatic Resource Management)) is nice, short and straightforward when using only one AutoCloseable resource. However, I am not sure what is the correct idiom when I need to declare multiple resources that are dependent on each other, for example a FileWriter and a BufferedWriter that wraps it. Of course, this question concerns any case when some AutoCloseable resources are wrapped, not only these two specific classes.

我提出了以下三种选择:

I came up with the three following alternatives:

我看到的天真习惯就是申报只有ARM托管变量中的顶级包装器:

The naive idiom I have seen is to declare only the top-level wrapper in the ARM-managed variable:

static void printToFile1(String text, File file) {
    try (BufferedWriter bw = new BufferedWriter(new FileWriter(file))) {
        bw.write(text);
    } catch (IOException ex) {
        // handle ex
    }
}

这很好而且很短,但它已经坏了。由于底层 FileWriter 未在变量中声明,因此永远不会在生成的 finally 块中直接关闭它。它只能通过包装 BufferedWriter close 方法关闭。问题是,如果从 bw 的构造函数抛出异常,则不会调用其 close 并且因此,基础 FileWriter 将不会关闭

This is nice and short, but it is broken. Because the underlying FileWriter is not declared in a variable, it will never be closed directly in the generated finally block. It will be closed only through the close method of the wrapping BufferedWriter. The problem is, that if an exception is thrown from the bw's constructor, its close will not be called and therefore the underlying FileWriter will not be closed.

static void printToFile2(String text, File file) {
    try (FileWriter fw = new FileWriter(file);
            BufferedWriter bw = new BufferedWriter(fw)) {
        bw.write(text);
    } catch (IOException ex) {
        // handle ex
    }
}

这里,底层资源和包装资源都是在ARM管理的变量中声明的,所以它们都肯定会被关闭,但底层的 fw.close() 将被调用两次:不仅直接,还通过包装 bw.close()

Here, both the underlying and the wrapping resource are declared in the ARM-managed variables, so both of them will certainly be closed, but the underlying fw.close() will be called twice: not only directly, but also through the wrapping bw.close().

对于这两个实现 Closeable (这是 AutoCloseable ),其合约规定允许多次调用 close

This should not be a problem for these two specific classes that both implement Closeable (which is a subtype of AutoCloseable), whose contract states that multiple calls to close are permitted:


关闭此流并释放与其关联的所有系统资源。如果流已经关闭,则调用此方法无效。

Closes this stream and releases any system resources associated with it. If the stream is already closed then invoking this method has no effect.

但是,在一般情况下,我可以拥有实现的资源只有 AutoCloseable (而不是可关闭),这不能保证关闭可以多次调用:

However, in a general case, I can have resources that implement only AutoCloseable (and not Closeable), which doesn't guarantee that close can be called multiple times:


请注意,与java.io.Closeable的close方法不同,此close方法不是要求是幂等的。换句话说,多次调用此close方法可能会产生一些可见的副作用,这与Closeable.close不同,如果多次调用则需要无效。但是,强烈建议使用此接口的实现者使其close方法具有幂等性。

Note that unlike the close method of java.io.Closeable, this close method is not required to be idempotent. In other words, calling this close method more than once may have some visible side effect, unlike Closeable.close which is required to have no effect if called more than once. However, implementers of this interface are strongly encouraged to make their close methods idempotent.



3)



3)

static void printToFile3(String text, File file) {
    try (FileWriter fw = new FileWriter(file)) {
        BufferedWriter bw = new BufferedWriter(fw);
        bw.write(text);
    } catch (IOException ex) {
        // handle ex
    }
}

这个版本在理论上应该是正确的,因为只有 fw 表示需要清理的真实资源。 bw 本身不包含任何资源,它只委托给 fw ,所以它应该只够关闭底层的 fw

This version should be theoretically correct, because only the fw represents a real resource that needs to be cleaned up. The bw doesn't itself hold any resource, it only delegates to the fw, so it should be sufficient to only close the underlying fw.

另一方面,语法有点不规则,Eclipse也发布了警告,我认为这是一个误报,但它仍然是一个必须处理的警告:

On the other hand, the syntax is a bit irregular and also, Eclipse issues a warning, which I believe is a false alarm, but it is still a warning that one has to deal with:


资源泄漏:'bw '永远不会关闭

Resource leak: 'bw' is never closed






那么,采用哪种方法?或者我是否错过了正确的其他成语?

推荐答案

这是我对替代方案:

try (BufferedWriter bw = new BufferedWriter(new FileWriter(file))) {
    bw.write(text);
}

对我而言,15年前从传统C ++到Java的最好的事情是你可以相信你的计划。即使事情发生在他们常常做的事情中,我也希望其余的代码能够达到最好的行为和玫瑰的味道。实际上, BufferedWriter 可能会抛出异常。例如,耗尽内存并不罕见。对于其他装饰器,您是否知道哪个 java.io 包装器类从其构造函数中抛出一个已检查的异常?我不。如果你依赖那种晦涩的知识,那么代码的可理解性就不会很好。

For me, the best thing coming to Java from traditional C++ 15 years ago was that you could trust your program. Even if things are in the muck and going wrong, which they often do, I want the rest of the code to be on best behaviour and smelling of roses. Indeed, the BufferedWriter might throw an exception here. Running out of memory wouldn't be unusual, for instance. For other decorators, do you know which of the java.io wrapper classes throw a checked exception from their constructors? I don't. Doesn't do code understandability much good if you rely upon that sort of obscure knowledge.

还有破坏。如果存在错误情况,那么您可能不希望将垃圾清除到需要删除的文件(未显示的代码)。虽然,当然,删除文件也是另一个有趣的错误处理操作。

Also there's the "destruction". If there is an error condition, then you probably don't want to be flushing rubbish to a file that needs deleting (code for that not shown). Although, of course, deleting the file is also another interesting operation to do as error handling.

一般来说你想要最后块尽可能短且可靠。添加刷新对此目标没有帮助。对于许多版本,JDK中的一些缓冲类有一个错误, flush 中的 close 中的异常导致<$不能调用装饰对象上的c $ c> close 。虽然已经修复了一段时间,但是可以从其他实现中预期。

Generally you want finally blocks to be as short and reliable as possible. Adding flushes does not help this goal. For many releases some of the buffering classes in the JDK had a bug where an exception from flush within close caused close on the decorated object not be called. Whilst that has been fixed for some time, expect it from other implementations.

try (
    FileWriter fw = new FileWriter(file);
    BufferedWriter bw = new BufferedWriter(fw)
) {
    bw.write(text);
}

我们仍在冲洗隐含的finally块(现在重复关闭 - 这会因为你添加更多装饰器而变得更糟),但结构是安全的,我们必须隐含finally块,所以即使是失败的 flush 不会阻止资源释放。

We're still flushing in the implicit finally block (now with repeated close - this gets worse as you add more decorators), but the construction is safe and we have to implicit finally blocks so even a failed flush doesn't prevent resource release.

try (FileWriter fw = new FileWriter(file)) {
    BufferedWriter bw = new BufferedWriter(fw);
    bw.write(text);
}

这里有一个错误。应该是:

There's a bug here. Should be:

try (FileWriter fw = new FileWriter(file)) {
    BufferedWriter bw = new BufferedWriter(fw);
    bw.write(text);
    bw.flush();
}

一些执行不佳的装饰器实际上是资源,需要可靠地关闭。还有一些流可能需要以特定方式关闭(可能它们正在进行压缩并需要写入位来完成,并且不能只刷新所有内容。

Some poorly implemented decorators are in fact resource and will need to be closed reliably. Also some streams may need to be closed in a particular way (perhaps they are doing compression and need to write bits to finish off, and can't just flush everything.

虽然3是技术上优越的解决方案,但软件开发的原因使2成为更好的选择。但是,试用资源仍然是一个不充分的修复,你应该坚持 Execute Around idiom ,它应该在Java SE中具有更清晰的闭包语法8。

Although 3 is a technically superior solution, software development reasons make 2 the better choice. However, try-with-resource is still an inadequate fix and you should stick with the Execute Around idiom, which should have a clearer syntax with closures in Java SE 8.

这篇关于在try-with-resources块中管理多个链式资源的正确习惯用法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆