为啥 try-with-resources 不能与字段变量一起使用?

Posted

技术标签:

【中文标题】为啥 try-with-resources 不能与字段变量一起使用?【英文标题】:Why doesn't try-with-resources work with field variables?为什么 try-with-resources 不能与字段变量一起使用? 【发布时间】:2013-06-21 12:31:48 【问题描述】:

这是我关于 SO 的第一个问题,我很困惑还没有类似的问题!

所以问题是:

为什么 try-with-resources 不能与字段变量一起使用?

或者换句话说:为什么我总是需要一个局部变量?

这里有一些示例代码:

public class FileWriteTest 

    public FileWriter file;

    public void workingDemo() 

        try(FileWriter file = new FileWriter(new File("someFilePath")) 
            // do something
         catch (IOException e) 
            e.printStackTrace();
        

    

    public void notWorkingDemo() 

        file = null;

        try(file = new FileWriter(new File("someFilePath")) 
            // do something
         catch (IOException e) 
            e.printStackTrace();
        

    


谁能解释一下为什么会有这个约定?

【问题讨论】:

【参考方案1】:

在 try-with-resources 块的执行过程中,可以随时更改实例变量。这将破坏其不变量并阻止清理。请注意,出于同样的原因,局部变量是隐含的 final。

顺便说一句,一个更好的问题是,为什么Java 强制我们声明一个局部变量,即使我们没有在块中引用它。例如,C# 不需要这个。

更新:在第 9 版中,Java 不再强迫我们:

private final Some obj = new Some();

try (obj)  
  // obj captured in a hidden local variable, resource closed in the end

【讨论】:

【参考方案2】:

我怀疑设计师认为使用字段是个坏主意,因为这会使对象逃离使用区域。即它仅在 try 块中有效,因此您应该无法在其他任何地方访问它。

【讨论】:

也可以这样逃走。关键是有一个 final 变量,它保证在 try-with-resources 结束之前保持相同的值。 @MarkoTopolnik try 字段是否已隐式设为最终字段? 是的,刚刚试过。在 try-with-resources 中声明的变量是隐式的 final。具体来说,Eclipse 给出了这个错误:“无法分配 try-with-resources 语句的资源 ”。与最终变量不同。 @MarkoTopolnik 我不知道。这说得通。然后我又希望变量是隐式最终的,除非使用关键字使它们成为非最终的。例如var 他们几乎肯定会将他们的 vars 保留为最终版本,除非他们特别需要更改。除此之外,它可能会阻止涉及可变变量的习语。令人难以置信的是,我从带有最终变量的代码中减少了多少认知压力!【参考方案3】:

Java Language Specification 中的Section 14.20.3 声明它仅适用于局部变量。

这是为什么?我的猜测是检查明确的赋值和转义(局部变量不会转义到另一个方法的范围内)。一个字段可以在类中的任何地方初始化。我的猜测是,通过验证它是一个局部变量,分析起来要简单得多。

【讨论】:

【参考方案4】:

在 Java 9 中,他们增加了对带有变量的资源的 try 支持。

    // Original try-with-resources statement from JDK 7 or 8
    try (Resource r1 = resource1;
         Resource r2 = resource2) 
        // Use of resource1 and resource 2 through r1 and r2.
    

    // New and improved try-with-resources statement in JDK 9
    try (resource1;
         resource2) 
        // Use of resource1 and resource 2.
    

https://blogs.oracle.com/darcy/more-concise-try-with-resources-statements-in-jdk-9

【讨论】:

【参考方案5】:

首先,我认为拥有一个在多个地方使用的变量/资源是不好的做法。如果它没有在try 中打开,那么你以后不能关闭它,如果它在那里打开,那么你就不需要一个非局部变量。 这导致“第二个”:如果您已经打开了一个资源,那么您需要在其他地方明确关闭它,否则自动关闭将不知道它是否打开。

因此,恕我直言,只有按照规范中指定的方式处理它才有意义。

【讨论】:

【参考方案6】:

从 Java 9 开始,无需在 try-with-resources 块中使用局部变量。见here。

【讨论】:

【参考方案7】:

这可能与语言规范的一致性有关。 每当在两个括号之间声明一个变量时,它都被封装在内部,不能从外部访问:

anything

int var;


// cannot access var from here!

为什么要尝试 是一个例外?

【讨论】:

以上是关于为啥 try-with-resources 不能与字段变量一起使用?的主要内容,如果未能解决你的问题,请参考以下文章

1.9 try-with-resources 优先于try-finally

try-with-resources 调用 close() 失败

Redis分布式锁的try-with-resources实现

第9项:尽量使用try-with-resources而不是try-finally(Prefer try-with-resources to try-finally)

try-with-resources 语法简介

为啥 NSFileManager 不能与 UIDocumentBrowserViewController 一起使用?