当您要使用某些AutoClosable
对象时,应使用try-with-resources。好。但是,如果我想编写返回AutoClosable
的方法,该怎么办?在某个地方创建了AutoCloseable对象或从中接收到AutoCloseable对象之后,应将其关闭,以防出现异常,例如:
public static AutoCloseable methodReturningAutocloseable() {
AutoCloseable autoCloseable = ... // create some AutoClosable
try {
... // some work
}
catch (Throwable exception) {
autoCloseable.close();
throw exception;
}
return autoCloseable;
}
如果不编写try/catch
块,则会泄漏资源,如果// some work
行中有异常,则该autoCloseable对象将保留。
但是这个try/catch
是不够的,因为autoCloseable.close()
也会抛出异常(根据设计)。因此,以上代码转换为
public static AutoCloseable methodReturningAutocloseable() {
AutoCloseable autoCloseable = ... // create some autoclosable
try {
... // some work
}
catch (Throwable exception) {
try {
autoCloseable.close();
}
catch (Throwable exceptionInClose) {
exception.addSuppressed(exceptionInClose);
throw exception;
}
throw exception;
}
return autoCloseable;
}
很多样板。在Java中有更好的方法吗?
答案 0 :(得分:3)
有很多方法可以解决。
AutoCloseable
中,并将其放入您的try-with-resource中。编辑:我想我会重新回答一下,添加一些有趣的示例代码。
围绕习语执行
简单,最佳的解决方案。不幸的是,Java库使用得很少(AccessController.doPrivileged
是一个很大的例外),并且约定也没有很好的建立。与以往一样,Java不支持功能的检查异常使事情变得棘手。我们不能使用java.util.function
,而必须发明自己的功能接口。
// Like Consumer, but with an exception.
interface Use<R, EXC extends Exception> {
void use(R resource) throws EXC;
}
public static void withThing(String name, Use<InputStream,IOException> use) throws IOException {
try (InputStream in = new FileInputStream(name)) {
use.use(in);
}
}
又好又简单。无需担心客户端代码会搞乱资源处理,因为它不会这样做。很好。
经过修改的尝试使用资源作为库功能,在尝试使用资源中作为代理AutoCloseable
实现
这将变得丑陋。我们需要将获取,发布和初始化作为lambda传递。直接在此方法中创建资源会打开一个小窗口,意外的异常会导致泄漏。
public static InputStream newThing(String name) throws IOException {
return returnResource(
() -> new FileInputStream(name),
InputStream::close,
in -> {
int ignore = in.read(); // some work
}
);
}
returnResource
的一般实现如下所示。骇客,因为try-with-resource不支持这种事情,而Java库也不很好地支持检查的异常。请注意,仅限于一种异常(您可以将未检查的异常用于没有检查的异常)。
interface Acquire<R, EXC extends Exception> {
R acquire() throws EXC;
}
// Effectively the same as Use, but different.
interface Release<R, EXC extends Exception> {
void release(R resource) throws EXC;
}
public static <R, EXC extends Exception> R returnResource(
Acquire<R, EXC> acquire, Release<R, EXC> release, Use<R, EXC> initialize
) throws EXC {
try (var adapter = new AutoCloseable() { // anonymous classes still define type
private R resource = acquire.acquire();
R get() {
return resource;
}
void success() {
resource = null;;
}
public void close() throws EXC {
if (resource != null) {
release.release(resource);
}
}
}) {
R resource = adapter.get();
initialize.use(resource);
adapter.success();
return resource;
}
}
如果我们从资源的构造中分离出正在构造资源的论点,这也许会更干净。
public static InputStream newThing(String name) throws IOException {
return returnResource(
name,
FileInputStream::new,
InputStream::close,
in -> {
int ignore = in.read(); // some work
}
);
}
// Like Function, but with a more descriptive name for a functional interface.
interface AcquireFrom<T, R, EXC extends Exception> {
R acquire(T t) throws EXC;
}
public static <T, R, EXC extends Exception> R returnResource(
T t, AcquireFrom<T, R, EXC> acquire, Release<R, EXC> release, Use<R, EXC> initialize
) throws EXC {
return returnResource(() -> acquire.acquire(t), release, initialize);
}
因此,总而言之,以下几点很痛苦:
java.util.function
不支持检查的异常。AutoCloseable::close
声明它抛出Exception
而不是类型的类型参数。