清理多个不可关闭资源时减少嵌套
Reduce nesting when cleaning up multiple non-Closeable resources
我有一个Closeable
需要在close()
方法中清理多个资源。每个资源都是我无法修改的final
class。 None 个包含的资源是 Closeable
或 AutoCloseable
。我还需要打电话给super.close()
。所以看来我无法使用 try-with-resources
处理任何资源*。我当前的实现看起来像这样:
public void close() throws IOException {
try {
super.close();
} finally {
try {
container.shutdown();
} catch (final ShutdownException e) {
throw new IOException("ShutdownException: ", e);
} finally {
try {
client.closeConnection();
} catch (final ConnectionException e) {
throw new IOException("Handling ConnectionException: ", e);
}
}
}
}
我更喜欢嵌套不那么疯狂的解决方案,但我不知道如何利用 try-with-resources
或任何其他功能来做到这一点。 Code sandwiches don't seem to help here since I'm not using the resources at all, just cleaning them up. Since the resources aren't Closeable
, it's unclear how I could use the recommended solutions in Java io ugly try-finally block.
* 即使 super
class 是 Closeable
,我也不能在 try-with-resources
中使用 super
因为 super
只是句法糖而不是真正的 Java Object
.
这对 try-with-resources
来说是一个很好的(尽管不是正统的)案例。首先,您需要创建一些接口:
interface ContainerCleanup extends AutoCloseable {
@Override
void close() throws ShutdownException;
}
interface ClientCleanup extends AutoCloseable {
@Override
void close() throws ConnectionException;
}
如果这些接口仅在当前class中使用,我建议将它们设为内部接口。但如果您在多个 class 中使用它们,它们也可以用作 public 实用程序接口。
然后在你的close()
方法中你可以做:
public void close() throws IOException {
final Closeable ioCleanup = new Closeable() {
@Override
public void close() throws IOException {
YourCloseable.super.close();
}
};
final ContainerCleanup containerCleanup = new ContainerCleanup() {
@Override
public void close() throws ShutdownException {
container.shutdown();
}
};
final ClientCleanup clientCleanup = new ClientCleanup() {
@Override
public void close() throws ConnectionException {
client.closeConnection();
}
};
// Resources are closed in the reverse order in which they are declared,
// so reverse the order of cleanup classes.
// For more details, see Java Langauge Specification 14.20.3 try-with-resources:
// https://docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.20.3
try (clientCleanup; containerCleanup; ioCleanup) {
// try-with-resources only used to ensure that all resources are cleaned up.
} catch (final ShutdownException e) {
throw new IOException("Handling ShutdownException: ", e);
} catch (final ConnectionException e) {
throw new IOException("Handling ConnectionException: ", e);
}
}
当然,使用 Java 8 个 lambda 表达式会变得更加优雅和简洁:
public void close() throws IOException {
final Closeable ioCleanup = () -> super.close();
final ContainerCleanup containerCleanup = () -> container.shutdown();
final ClientCleanup clientCleanup = () -> client.closeConnection();
// Resources are closed in the reverse order in which they are declared,
// so reverse the order of cleanup classes.
// For more details, see Java Langauge Specification 14.20.3 try-with-resources:
// https://docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.20.3
try (clientCleanup; containerCleanup; ioCleanup) {
// try-with-resources only used to ensure that all resources are cleaned up.
} catch (final ShutdownException e) {
throw new IOException("Handling ShutdownException: ", e);
} catch (final ConnectionException e) {
throw new IOException("Handling ConnectionException: ", e);
}
}
这消除了所有疯狂的嵌套,并且具有保存被抑制的异常的额外好处。在您的情况下,如果 client.closeConnection()
抛出,我们永远不会知道以前的方法是否抛出任何异常。所以堆栈跟踪看起来像这样:
Exception in thread "main" java.io.IOException: Handling ConnectionException:
at Main$YourCloseable.close(Main.java:69)
at Main.main(Main.java:22)
Caused by: Main$ConnectionException: Failed to close connection.
at Main$Client.closeConnection(Main.java:102)
at Main$YourCloseable.close(Main.java:67)
... 1 more
通过使用 try-with-resources
,Java 编译器生成代码来处理被抑制的异常,因此我们将在堆栈跟踪中看到它们,如果需要,我们甚至可以在调用代码中处理它们至:
Exception in thread "main" java.io.IOException: Failed to close super.
at Main$SuperCloseable.close(Main.java:104)
at Main$YourCloseable.access[=14=]1(Main.java:35)
at Main$YourCloseable .close(Main.java:49)
at Main$YourCloseable.close(Main.java:68)
at Main.main(Main.java:22)
Suppressed: Main$ShutdownException: Failed to shut down container.
at Main$Container.shutdown(Main.java:140)
at Main$YourCloseable.close(Main.java:55)
at Main$YourCloseable.close(Main.java:66)
... 1 more
Suppressed: Main$ConnectionException: Failed to close connection.
at Main$Client.closeConnection(Main.java:119)
at Main$YourCloseable.close(Main.java:61)
at Main$YourCloseable.close(Main.java:66)
... 1 more
注意事项
如果清理的顺序很重要,您需要在 reverse[=] 中声明您的资源清理 classes/lambdas 50=] 订购您想要的 运行。我建议为此效果添加评论(就像我提供的那样)。
如果任何异常被抑制,该异常的catch
块将不执行。在这些情况下,最好更改 lambda 来处理异常:
final Closeable containerCleanup = () -> {
try {
container.shutdown();
} catch (final ShutdownException e) {
// Handle shutdown exception
throw new IOException("Handling shutdown exception:", e);
}
}
处理 lambda 内部的异常确实开始添加一些嵌套,但嵌套不像原来的那样是递归的,所以它永远只有一层深。
即使有这些警告,我相信这里的自动抑制异常处理、简洁、优雅、可读性和减少嵌套(特别是如果您有 3 个或更多资源需要清理)的优点远远超过缺点。
我有一个Closeable
需要在close()
方法中清理多个资源。每个资源都是我无法修改的final
class。 None 个包含的资源是 Closeable
或 AutoCloseable
。我还需要打电话给super.close()
。所以看来我无法使用 try-with-resources
处理任何资源*。我当前的实现看起来像这样:
public void close() throws IOException {
try {
super.close();
} finally {
try {
container.shutdown();
} catch (final ShutdownException e) {
throw new IOException("ShutdownException: ", e);
} finally {
try {
client.closeConnection();
} catch (final ConnectionException e) {
throw new IOException("Handling ConnectionException: ", e);
}
}
}
}
我更喜欢嵌套不那么疯狂的解决方案,但我不知道如何利用 try-with-resources
或任何其他功能来做到这一点。 Code sandwiches don't seem to help here since I'm not using the resources at all, just cleaning them up. Since the resources aren't Closeable
, it's unclear how I could use the recommended solutions in Java io ugly try-finally block.
* 即使 super
class 是 Closeable
,我也不能在 try-with-resources
中使用 super
因为 super
只是句法糖而不是真正的 Java Object
.
这对 try-with-resources
来说是一个很好的(尽管不是正统的)案例。首先,您需要创建一些接口:
interface ContainerCleanup extends AutoCloseable {
@Override
void close() throws ShutdownException;
}
interface ClientCleanup extends AutoCloseable {
@Override
void close() throws ConnectionException;
}
如果这些接口仅在当前class中使用,我建议将它们设为内部接口。但如果您在多个 class 中使用它们,它们也可以用作 public 实用程序接口。
然后在你的close()
方法中你可以做:
public void close() throws IOException {
final Closeable ioCleanup = new Closeable() {
@Override
public void close() throws IOException {
YourCloseable.super.close();
}
};
final ContainerCleanup containerCleanup = new ContainerCleanup() {
@Override
public void close() throws ShutdownException {
container.shutdown();
}
};
final ClientCleanup clientCleanup = new ClientCleanup() {
@Override
public void close() throws ConnectionException {
client.closeConnection();
}
};
// Resources are closed in the reverse order in which they are declared,
// so reverse the order of cleanup classes.
// For more details, see Java Langauge Specification 14.20.3 try-with-resources:
// https://docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.20.3
try (clientCleanup; containerCleanup; ioCleanup) {
// try-with-resources only used to ensure that all resources are cleaned up.
} catch (final ShutdownException e) {
throw new IOException("Handling ShutdownException: ", e);
} catch (final ConnectionException e) {
throw new IOException("Handling ConnectionException: ", e);
}
}
当然,使用 Java 8 个 lambda 表达式会变得更加优雅和简洁:
public void close() throws IOException {
final Closeable ioCleanup = () -> super.close();
final ContainerCleanup containerCleanup = () -> container.shutdown();
final ClientCleanup clientCleanup = () -> client.closeConnection();
// Resources are closed in the reverse order in which they are declared,
// so reverse the order of cleanup classes.
// For more details, see Java Langauge Specification 14.20.3 try-with-resources:
// https://docs.oracle.com/javase/specs/jls/se8/html/jls-14.html#jls-14.20.3
try (clientCleanup; containerCleanup; ioCleanup) {
// try-with-resources only used to ensure that all resources are cleaned up.
} catch (final ShutdownException e) {
throw new IOException("Handling ShutdownException: ", e);
} catch (final ConnectionException e) {
throw new IOException("Handling ConnectionException: ", e);
}
}
这消除了所有疯狂的嵌套,并且具有保存被抑制的异常的额外好处。在您的情况下,如果 client.closeConnection()
抛出,我们永远不会知道以前的方法是否抛出任何异常。所以堆栈跟踪看起来像这样:
Exception in thread "main" java.io.IOException: Handling ConnectionException:
at Main$YourCloseable.close(Main.java:69)
at Main.main(Main.java:22)
Caused by: Main$ConnectionException: Failed to close connection.
at Main$Client.closeConnection(Main.java:102)
at Main$YourCloseable.close(Main.java:67)
... 1 more
通过使用 try-with-resources
,Java 编译器生成代码来处理被抑制的异常,因此我们将在堆栈跟踪中看到它们,如果需要,我们甚至可以在调用代码中处理它们至:
Exception in thread "main" java.io.IOException: Failed to close super.
at Main$SuperCloseable.close(Main.java:104)
at Main$YourCloseable.access[=14=]1(Main.java:35)
at Main$YourCloseable .close(Main.java:49)
at Main$YourCloseable.close(Main.java:68)
at Main.main(Main.java:22)
Suppressed: Main$ShutdownException: Failed to shut down container.
at Main$Container.shutdown(Main.java:140)
at Main$YourCloseable.close(Main.java:55)
at Main$YourCloseable.close(Main.java:66)
... 1 more
Suppressed: Main$ConnectionException: Failed to close connection.
at Main$Client.closeConnection(Main.java:119)
at Main$YourCloseable.close(Main.java:61)
at Main$YourCloseable.close(Main.java:66)
... 1 more
注意事项
如果清理的顺序很重要,您需要在 reverse[=] 中声明您的资源清理 classes/lambdas 50=] 订购您想要的 运行。我建议为此效果添加评论(就像我提供的那样)。
如果任何异常被抑制,该异常的
catch
块将不执行。在这些情况下,最好更改 lambda 来处理异常:final Closeable containerCleanup = () -> { try { container.shutdown(); } catch (final ShutdownException e) { // Handle shutdown exception throw new IOException("Handling shutdown exception:", e); } }
处理 lambda 内部的异常确实开始添加一些嵌套,但嵌套不像原来的那样是递归的,所以它永远只有一层深。
即使有这些警告,我相信这里的自动抑制异常处理、简洁、优雅、可读性和减少嵌套(特别是如果您有 3 个或更多资源需要清理)的优点远远超过缺点。