异常安全 return 可自动关闭的对象
Exception-safely return Autoclosable object
当您想使用某些 AutoClosable
对象时,您应该使用 try-with-resources。行。但是如果我想写方法,那个 returns AutoClosable
怎么办?在你创建或从某处接收到一个 AutoCloseable 对象后,你应该关闭它以防出现异常,如下所示:
public static AutoCloseable methodReturningAutocloseable() {
AutoCloseable autoCloseable = ... // create some AutoClosable
try {
... // some work
}
catch (Throwable exception) {
autoCloseable.close();
throw exception;
}
return autoCloseable;
}
如果你不写 try/catch
块你会泄漏资源,autoCloseable 对象持有,以防 // some work
行中的异常。
但是这个 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 中有更好的方法吗?
有几个走近了。
- 使用Execute Around idiom。重新制定界面以简化客户端实施并消除问题。
- 忽略这个问题。听起来很傻,但这通常是用 I/O 流装饰器包装时发生的情况。
- 包裹在代理中
AutoCloseable
并将其放入您的 try-with-resource。
- 写出 try-with-resource 使用 try-catch 和 try-finally 的等价物。 (我不会 - 讨厌。)
- 将修改后的 try-with-resource 编写为库功能。此代码将成为 Execute Around 的客户端。
- 用 try-catch 和 try-finally 编写异常处理老派风格。
编辑: 我想我会重新审视答案,添加一些示例代码来取乐。
Execute Around 成语
最简单的最佳解决方案。不幸的是,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);
}
}
漂亮又简单。无需担心客户端代码会搞乱资源处理,因为它不会这样做。不错
修改后的 try-with-resource 作为库功能在 try-with-resource
中实现为代理 AutoCloseable
它会变得丑陋。我们需要将获取、发布和初始化作为 lambda 传递。直接在此方法中创建资源会打开一个小 window,意外异常会导致泄漏。
public static InputStream newThing(String name) throws IOException {
return returnResource(
() -> new FileInputStream(name),
InputStream::close,
in -> {
int ignore = in.read(); // some work
}
);
}
returnResource
的一般实现如下所示。一个 hack,因为 try-with-resource 不支持这种事情并且 Java 库不支持检查异常。注意仅限于一种异常(没有检查异常可以使用unchecked exception)。
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
不支持检查异常。
- Java 库不支持 Execute Around。
AutoCloseable::close
声明它抛出 Exception
而不是作为该类型的类型参数。
- 已检查没有总和类型的异常。
- 一般的 Java 语言语法。
当您想使用某些 AutoClosable
对象时,您应该使用 try-with-resources。行。但是如果我想写方法,那个 returns AutoClosable
怎么办?在你创建或从某处接收到一个 AutoCloseable 对象后,你应该关闭它以防出现异常,如下所示:
public static AutoCloseable methodReturningAutocloseable() {
AutoCloseable autoCloseable = ... // create some AutoClosable
try {
... // some work
}
catch (Throwable exception) {
autoCloseable.close();
throw exception;
}
return autoCloseable;
}
如果你不写 try/catch
块你会泄漏资源,autoCloseable 对象持有,以防 // some work
行中的异常。
但是这个 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 中有更好的方法吗?
有几个走近了。
- 使用Execute Around idiom。重新制定界面以简化客户端实施并消除问题。
- 忽略这个问题。听起来很傻,但这通常是用 I/O 流装饰器包装时发生的情况。
- 包裹在代理中
AutoCloseable
并将其放入您的 try-with-resource。 - 写出 try-with-resource 使用 try-catch 和 try-finally 的等价物。 (我不会 - 讨厌。)
- 将修改后的 try-with-resource 编写为库功能。此代码将成为 Execute Around 的客户端。
- 用 try-catch 和 try-finally 编写异常处理老派风格。
编辑: 我想我会重新审视答案,添加一些示例代码来取乐。
Execute Around 成语
最简单的最佳解决方案。不幸的是,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);
}
}
漂亮又简单。无需担心客户端代码会搞乱资源处理,因为它不会这样做。不错
修改后的 try-with-resource 作为库功能在 try-with-resource
中实现为代理AutoCloseable
它会变得丑陋。我们需要将获取、发布和初始化作为 lambda 传递。直接在此方法中创建资源会打开一个小 window,意外异常会导致泄漏。
public static InputStream newThing(String name) throws IOException {
return returnResource(
() -> new FileInputStream(name),
InputStream::close,
in -> {
int ignore = in.read(); // some work
}
);
}
returnResource
的一般实现如下所示。一个 hack,因为 try-with-resource 不支持这种事情并且 Java 库不支持检查异常。注意仅限于一种异常(没有检查异常可以使用unchecked exception)。
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
不支持检查异常。- Java 库不支持 Execute Around。
AutoCloseable::close
声明它抛出Exception
而不是作为该类型的类型参数。- 已检查没有总和类型的异常。
- 一般的 Java 语言语法。