在Java中,我们遇到了许多成功加载一个资源但无法加载第二个资源的情况。
我们最终得出了一个模式:
public class MultipleResourceHolder implements AutoCloseable {
private final AutoCloseable resource1;
private final AutoCloseable resource2;
MultipleResourceHolder() {
try {
resource1 = // create it
resource2 = // create it
} catch (Throwable t) {
close();
throw t;
}
}
@Override
public void close() {
if (resource1 != null) {
try {
resource1.close();
} catch (Exception e) {
// handle it
}
}
if (resource2 != null) {
try {
resource2.close();
} catch (Exception e) {
// handle it
}
}
}
}
这里显然有很多样板。如果你的类还扩展了一些拥有自己资源的其他类,那么它会再次变得更加丑陋。此外,让对象靠近本身也会感觉不对。
有更优雅的方法吗?我已经尝试了一种CloseableList类型的方法,你将对象添加到列表中,它肯定会稍微整理close(),但构造函数本身并没有更好,它只是为了清理而向类中添加一个字段。 :/
答案 0 :(得分:2)
没有任何特别好的方式。 try-with-resources模式负责处理一个巨大的量的样板(在人们不在的情况下人们通常会出错)在它工作的情况下,但它并不总是有用。
另请注意,如果构造函数抛出,则链接构造函数调用没有合适的方法来安全地创建资源而没有泄漏风险,可继承类的构造函数也不能以一种方式创建资源,如果派生的 - 类构造函数抛出。我建议与在基础构造函数中调用虚方法的常规建议相反,让基础构造函数创建资源的唯一安全方法是要求派生类在从基础构造函数调用的方法中完成所有实际设置工作,这样派生类构造函数不做任何工作,因此不能抛出。遗憾的是,派生类无法将其字段声明为final
,但我不知道有什么好的解决方法。
经过进一步的考虑,如果基类定义了一个包含Parameters
标志和(可能无所事事)success
的{{1}}类,则可以做得相当好。 } 方法;每个派生类应该从其父类的派生类中派生出一个参数保持类,包括它需要的任何其他参数。每个类都应该有一个Close
构造函数,它接受自己的派生参数类型,并包含一个公共静态工厂方法,它将构造一个合适的“参数”对象并将其传递给try-with-resources块中的构造函数。像:
protected
FancyClass create(params)
{
FancyClass result;
try (FancyClassParams params = new FancyClassParams(params))
{
FancyClassParams.init();
result = new FancyClass(FancyClassParams);
result.init(FancyClassParams);
FancyClassParams.success = true;
}
}
的构造函数不应该获取任何资源,并且足够简单以至于它永远不会失败;如果在调用主构造函数之前需要打开任何文件或以其他方式获取资源,那么应该在FancyClassParams
中完成。 init
的{{1}}方法应检查close
是否已设置,如果没有,则清除已获取的任何资源。
上述方法需要在每个派生类中使用相当多的代码,以确保始终按预期执行清理,但即使在构造过程中的任何允许点发生异常,也应该稳健地清理资源。