【问题标题】:Why does try-with-resource require a local variable?为什么 try-with-resource 需要一个局部变量?
【发布时间】:2013-05-11 10:18:27
【问题描述】:

关于我的问题Any risk in a AutoCloseable wrapper for java.util.concurrent.locks.Lock?,我想知道为什么 try-with-resource-statement 需要一个 named 局部变量。

我目前的使用情况如下:

try (AutoCloseableReentrantReadWiteLock.Lock l = _lock.writeLock()) {
    // do something
}        

变量l 在try 块内未使用,只会污染命名空间。据我所知,类似的 C# using-statement 不需要本地命名变量。

由于在 try 块结束时关闭了匿名局部变量,是否有任何原因无法支持以下内容?

try (_lock.writeLock()) {
    // do something
}        

【问题讨论】:

  • @McDowell Java 9 正在解决需要定义 new 变量的当前行为,方法是允许使用有效的 existing 最终变量。从您的链接看来,Java 9 将支持没有可见标识符的自动关闭资源。 (Discussed here.)

标签: java try-with-resources


【解决方案1】:

@McDowell 评论中的链接揭示了 Joe Darcy 在 a blog post comment 中的正确答案,他领导了 the Java Technology Specification,引入了 try-with-resources 声明:

在 JDK 7 中,我们从 try-with-resources 结构开始,例如 允许对资源使用通用表达式, 包括方法调用。然而,专家组早期发现 审查草案 (http://jcp.org/aboutJava/communityprocess/edr/jsr334/index.html) 那个

“未来可能的改变 [to the try-with-resources statemenbt] 是 放弃对将资源指定为一般资源的支持 表达。非平凡的规范和实现复杂性 源于允许将一般表达式用作资源。一种 可以是标识符或 PrimaryNoNewArray 可能就足够了。甚至更严格的限制 只允许一个标识符可以提供几乎所有额外的 允许完整表达式的实用程序(过度强制声明 一个新的资源变量)在一个低得多的边际实现和 规范影响。”

在 JDK 7 结束时,我们想要的是一个全新的变量声明 对于资源或现有的最终/有效最终变量。我们 7 只来得及提供前者;在 9 中,我们提供 后者也是。

【讨论】:

  • java9 仍然不支持 OP 的情况。
【解决方案2】:

在他们考虑的用例中,大多数都需要访问块内的资源,例如,打开文件-读/写文件-关闭文件。如果他们认为有很多用例未使用局部变量,他们就不会做出这个设计决定。

至于为什么 Lock 不能自动关闭,我认为 Doug Lea 并不太关心语法问题,他专注于解决难题。其他人总是可以在他的实用程序之上添加语法糖。

展望未来,try-with-resource 可能会过时,取而代之的是 lambda。例如

lock.withLock( ()->{ execute-while-holding-the-lock; } );

【讨论】:

  • +1 用于 lamda 用法。其余的都是投机的。虽然可能是正确的,但我更感兴趣的是是否有无法完成的原因。
  • 确定可以,只是工作量更大,他们认为不值得。
  • 恕我直言,这是对 lambda 的滥用。有 0 的好处,它使代码更难阅读。不要这样做。
  • "他们不会做出这个设计决定...":是的,他们会。因为时间不够。看我的回答。
  • 使用 lamda 表达式的想法很好,但有几个主要问题:1) 在调试器中单步执行代码变得很痛苦 2) 调用层次结构与 lamda 调用混淆 3) 内部代码完成lamdas 目前不是最理想的(至少在 Eclipse Neon 中)。
【解决方案3】:

尽管我希望不是这样,但其背后的基本原理是 try-with-resources 严格用于对必须处理的项目进行的操作。它需要一个命名变量,因为它希望您在块内时对该变量执行某些操作。我想这就像编译器说“如果你不打算实际使用资源,你为什么要尝试使用资源?”

现在你和我都非常清楚,我们不想实际使用该资源:相反,我们只是想确保在使用完它时将其关闭,这样我们就不会让开发人员四处锁定系统。但就像很多事情一样,他们必须做出设计决策,而且我们是少数,我们没有得到这个功能。

【讨论】:

  • am 使用资源 -- 因为它在 <init>()close() 中的副作用
  • 但是您没有在块内使用它,这是 try-with-resources 的主要用例。我也觉得很沮丧,但事实就是这样。您可以通过import static锁定您的课程来为自己节省一些空间。
  • 在块内不显式使用资源的原因有很多,例如它可能是一个锁;或保存在 ThreadLocal 中。
  • 我完全同意你的看法,斯特凡。正如我所说,我也觉得很沮丧。对于此功能的设计者来说,似乎 init 和 close 不被视为“在块中”。
  • 那绝对是个糟糕的决定。 try with resources 可用于进行块关闭。这么短视
【解决方案4】:

我认为无法使用局部变量是 try-with-resource 的触发因素。

在 Java 1.7 之前,您必须编写如下内容:

InputStream in = null;
try {
    in = ....;
} finally {
    if (in != null) {
        in.close();
    }
}

这里有两个缺点:

  1. finally 块很烦人,每个关闭资源都必须是 null 安全的
  2. 我们必须声明块外的资源才能在finally 块中访问它们。因此,我们扩大了可访问变量的范围,这是不好的做法。

Try-with-resource 语法解决了这两个问题:

  1. finally 块根本不需要。
  2. 资源变量只能在 try 块中访问,即应该知道它的位置。

这就是为什么可关闭资源必须是本地的。否则,try-with-resource 语法的主要缺点之一是“禁用”。

【讨论】:

  • 您提供了充分的理由在 try-with-resources 中进行 allow 声明。 OP 询问为什么它是必需的
  • 主要动机显然是避免大量样板文件,并为程序员提供一个现成的解决方案来解决相当复杂和细微的问题。我认为范围问题是最大的。整个故事的 10%。
  • 很抱歉造成混淆;我意识到ResourceSpecification 块引入了一个局部变量,我的问题是为什么它必须被命名。
  • 你能编辑你的答案吗?以目前的形式,它不能回答我的问题
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-24
  • 2015-11-26
  • 2019-06-12
  • 2015-08-29
  • 1970-01-01
相关资源
最近更新 更多