调用超级构造函数时尝试资源
Posted
技术标签:
【中文标题】调用超级构造函数时尝试资源【英文标题】:Try-with-resources when calling super constructor 【发布时间】:2016-06-07 07:36:45 【问题描述】:在构造函数中打开 InputStream
然后将其传递给超级构造函数时,是否有任何使用 try-with-resources 的好方法?
基本上我想做的是这样的:
public class A
public A(InputStream stream)
// Do something with the stream but don't close it since we didn't open it
public class B
public B(File file)
// We open the stream so we need to ensure it's properly closed
try (FileInputStream stream = new FileInputStream(file))
super(new FileInputStream(file));
但是,当然,因为super
必须是构造函数中的第一条语句,所以这是不允许的。有什么好的方法可以实现吗?
【问题讨论】:
我会让调用者将输入流提供给public B(InputStream in)
并关闭它。没有理由使派生类的通用性不如基类。
更通用,但使用起来也更麻烦。我可以同时支持这两者,但没有 B(File file)
构造函数不是一个选项。
在我看来,您的问题来自消耗 A 的构造函数中的流。如果不是这种情况,您只需将流存储在实例变量中并生成 A AutoClosable
。
@DidierL:那行不通,因为那时我不能在 try-with-resources 范围之外使用 B
的实例。
那么有一个单独的初始化器会是一个更好的解决方案,或者你应该favor composition over inheritance。
【参考方案1】:
考虑使用静态工厂方法而不是直接使用构造函数。至少将B
的构造函数设为私有,并创建方法如
private B(InputStream is)
super(is);
// Whatever else is needed
public static B newInstance(File file)
B result;
try (FileInputStream stream = new FileInputStream(file))
result = new B(stream);
// Further processing
return result;
【讨论】:
是的,看来这是要走的路【参考方案2】:另一种方法:
public class A
protected A()
// so he can't be called from the outside, subclass ensure that init is done properly.
public A(InputStream stream)
init(stream);
// not be able to call it from outside
protected final init(InputStream is)
//here goes the code
public class B
public B(File file)
// We open the stream so we need to ensure it's properly closed
try (FileInputStream stream = new FileInputStream(file))
init(stream);
我在这里发布这个作为一个可能的答案,但是在这里我正在考虑:
-
您可以更新 A 的代码
由于受保护的空 arg 构造函数,您正在将构造函数的代码移至 init 方法,因此只有子类必须正确处理对 init 的调用。有些人可能会认为这不是很好的设计。我的观点是,一旦您对某些东西进行子类化,您就必须在刚刚使用它时了解更多。
【讨论】:
正如你所说,它需要访问 A 的源代码,如果 A 中有最终变量需要从流中的内容初始化。 你应该创建init
final
,否则子类可能会覆盖它并破坏A
的构造函数。我认为这被称为构造函数泄漏或类似的东西。【参考方案3】:
很遗憾,我手头没有编译器可以测试,但你能不能按照下面的方法做。
public class B
private static InputStream file2stream(File f)
// We open the stream so we need to ensure it's properly closed
try (FileInputStream stream = new FileInputStream(file))
return stream;
catch(/*what you need to catch*/)
//cleanup
// possibly throw runtime exception
public B(File file)
super(file2stream(file))
【讨论】:
不幸的是,try
块的结尾在file2stream
的结尾之前到达,在它可以传递到super
之前关闭文件。
我可能误解了这个问题,但我认为这就是重点。我理解了这样一个问题,即唯一应该受 try 保护的是 InputStream 的构造,如果不是这种情况,应该如何理解它?
我想你不理解 Hank D 的评论。问题是在 try-with-resources 中,打开的资源(流)在块的末尾关闭。 return
语句并不能阻止这一点(或者没有指向 try
的点)。所以当file2stream
返回资源时,它已经关闭。还要注意 try-with-resources (用于关闭资源)和 try-catch 之间的区别,后者用于捕获异常,此处不讨论。
啊,那是我的问题,是的,这是一个问题。以上是关于调用超级构造函数时尝试资源的主要内容,如果未能解决你的问题,请参考以下文章
隐式超级构造函数 Person() 未定义。必须显式调用另一个构造函数?