调用超级构造函数时尝试资源

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 中有最终变量需要从流中的内容初始化。 你应该创建initfinal,否则子类可能会覆盖它并破坏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() 未定义。必须显式调用另一个构造函数?

如何:安全地调用具有不同参数的超级构造函数

爪哇。隐式超级构造函数 Employee() 未定义。必须显式调用另一个构造函数[重复]

如何在 Python 中调用超级构造函数?

如何在龙目岛调用超级构造函数

Javascript继承:调用超级构造函数还是使用原型链?