【问题标题】:Strange behavior with returns in try blocks in groovygroovy中try块中返回的奇怪行为
【发布时间】:2014-05-20 15:48:29
【问题描述】:

拿着这个sn-p:

def line = "asdf"

String foo() {
  try {
     //line = "qwer"
     return line
  } finally {
     line = "zxcv"
  }
}

println line
println foo()
println line

来自 .NET 背景,我希望打印行能够生成

asdf
asdf
zxcv

然而,在 groovy 中,它会产生

asdf
zxcv
asdf

这在某些方面让我感到困惑。

1) 为什么打印foo() 的值会产生zxcv?我希望 return 语句评估 line 并尝试返回 asdf,但在将 set line 返回到 asdf 之前,这不应该影响返回值。

2) 由于第二个输出是zxcv,我们必须接受line = "zxcv" 行正在执行。那么,为什么第三个输出会被设置回asdf

3) 取消注释 line = "qwer" 行会产生如下输出:

asdf
qwer
asdf

我认为这与任一模型都不一致。这看起来像是一些奇怪的范围界定行为,但我不知道这里的规则是什么。

使用 Groovy 2.2.2

编辑:啊哈!我从 groovy 控制台运行它。看来,如果我将它包装在一个类中并运行它,程序会产生预期的输出,即:

class MyClass {
    def line = "asdf"

    String foo() {
      try {
         //line = "qwer"
         return line
      } finally {
         line = "zxcv"
      }
    }

    def printStuff() {    
      println line
      println foo()
      println line
    }
}

new MyClass().printStuff()

产生

asdf
asdf
zxcv

我还发现了 Script -> Clear Script Context 菜单选项。清除脚本上下文后立即执行原来的sn -p时,控制台会抛出如下异常:

asdf
Exception thrown

groovy.lang.MissingPropertyException: No such property: line for class: ConsoleScript53

    at ConsoleScript53.foo(ConsoleScript53:6)

    at ConsoleScript53.run(ConsoleScript53:13)

后续运行产生不正确的输出

asdf
zxcv
asdf

有了这些新信息,GroovyConsole 中似乎有一些奇怪之处,它将line 提升到它的上下文中,并返回它而不是在顶部声明的line。或者其他的东西。知道这只是工具而不是语言的问题,这让我不想从我刚刚开始真正喜欢的语言中尖叫。

【问题讨论】:

  • 我无法重现这个。您能否制作一个 SSCCE 并包括您如何执行它(从命令行、在 groovy 控制台等)?

标签: groovy groovy-console


【解决方案1】:

http://groovy.codehaus.org/Scoping+and+the+Semantics+of+%22def%22会稍微解释一下情况。

基本上你应该删除第一行中的def 以获得.Net 结果。

但是我必须在这里与 Nathan 放在一起,您的程序通常不会产生您所说的输出。那是因为如果你读过链接的页面,你就会知道,foo() 中的line 引用了绑定,而foo() 之外的line 是一个局部变量,它与绑定无关.所以预期的结果是在执行return line 时得到一个MissingPropertyException。那是因为line 在您第一次在那里写之前不存在于绑定中。

要产生你看到的输出,你需要实际添加一个

line = "zxcv"

def 之前。这将确保line 在绑定中存在。在def 之后添加它会写入局部变量,您必须改用binding.line。为了让事情更简单,我在这里再次给出完整的程序:

line = "zxcv"
def line = "asdf"

String foo() {
  try {
     //line = "qwer"
     return line
  } finally {
     line = "zxcv"
  }
}

println line
println foo()
println line

因此,鉴于此已纠正程序,我可以回答您的问题:

  1. 打印foo() 的值会产生zxcv,因为这是进入函数时line 的值。如果您将第一行更改为使用例如123,那么您将得到123 而不是zxcv
  2. line = "zxcv" 行确实已执行并更改了绑定中存储的内容。但它不会改变方法将返回的内容,因此即使您将其更改为将 foobar 分配给 line,您也会得到相同的结果。由于第三个println 是从局部变量中读取而不是从绑定中读取,因此还解释了foo() 缺少的可见效果。
  3. 取消注释原始程序中的line = "qwer" 将确保您在绑定中有一些内容,因此您不会得到MissingPropertyException。但是foo() 仍然会返回在执行finally 块之前绑定中的内容,因此您会得到qwer

【讨论】:

  • 感谢您的解释!这一切都更有意义,链接也有帮助。多次运行脚本而不清除脚本上下文(从而重置绑定)是我没有得到MissingPropertyException的原因。
【解决方案2】:

在 Java(当然还有 Groovy)中,finally 块总是被执行,并且在方法结束之前执行,除非你调用 System.exit() 或 JVM 崩溃。

在 java 中,字符串是不可变的,每次更改字符串值时都会创建一个新对象。

所以第一行是,第二行是指不同的对象。

【讨论】:

  • 首先,如果 finally 块在返回调用之前退出,那么取消注释 line = "qwer" 应该不会对输出产生任何影响。其次,我没有改变任何字符串。我正在为变量line 分配一个字符串。我认为这不是正确的答案。
猜你喜欢
  • 2023-04-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-12
  • 1970-01-01
  • 1970-01-01
  • 2017-07-20
  • 2012-08-17
相关资源
最近更新 更多