【问题标题】:Groovy: what's the purpose of "def" in "def x = 0"?Groovy:“def x = 0”中“def”的目的是什么?
【发布时间】:2010-09-16 02:06:58
【问题描述】:

在下面这段代码(取自Groovy Semantics Manual page)中,为什么要在赋值前加上关键字def

def x = 0
def y = 5

while ( y-- > 0 ) {
    println "" + x + " " + y
    x++
}

assert x == 5

def 关键字可以删除,这个 sn-p 会产生相同的结果。那么关键字def作用是什么?

【问题讨论】:

    标签: groovy keyword


    【解决方案1】:

    它是基本脚本的语法糖。省略“def”关键字会将变量放入当前脚本的绑定中,并且 groovy 将其(大部分)视为全局范围的变量:

    x = 1
    assert x == 1
    assert this.binding.getVariable("x") == 1
    

    改用 def 关键字不会将变量放入脚本绑定中:

    def y = 2
    
    assert y == 2
    
    try {
        this.binding.getVariable("y") 
    } catch (groovy.lang.MissingPropertyException e) {
        println "error caught"
    } 
    

    打印:“错误捕获”

    在大型程序中使用 def 关键字很重要,因为它有助于定义可以找到变量的范围并有助于保持封装。

    如果您在脚本中定义了一个方法,它将无法访问在主脚本主体中使用“def”创建的变量,因为它们不在范围内:

     x = 1
     def y = 2
    
    
    public bar() {
        assert x == 1
    
        try {
            assert y == 2
        } catch (groovy.lang.MissingPropertyException e) {
            println "error caught"
        }
    }
    
    bar()
    

    打印“错误捕获”

    “y”变量不在函数范围内。 “x”在范围内,因为 groovy 将检查当前脚本的变量绑定。正如我之前所说,这只是一种语法糖,可以让快速而肮脏的脚本更快地输入(通常是一行)。

    大型脚本中的良好做法是始终使用“def”关键字,这样您就不会遇到奇怪的范围问题或干扰您不打算使用的变量。

    【讨论】:

      【解决方案2】:

      Ted's answer 非常适合脚本; Ben's answer 是类的标准。

      正如 Ben 所说,将其视为“Object”——但它更酷,因为它不会将您限制在 Object 方法中。这对导入有很好的影响。

      例如在这个 sn-p 中我必须导入 FileChannel

      // Groovy imports java.io.* and java.util.* automatically
      // but not java.nio.*
      
      import java.nio.channels.*
      
      class Foo {
          public void bar() {
              FileChannel channel = new FileInputStream('Test.groovy').getChannel()
              println channel.toString()
          }
      }
      
      new Foo().bar()
      

      例如但是在这里,只要所有内容都在类路径中,我就可以“使用它”

      // Groovy imports java.io.* and java.util.* automatically
      // but not java.nio.*
      class Foo {
          public void bar() {
              def channel = new FileInputStream('Test.groovy').getChannel()
              println channel.toString()
          }
      }
      
      new Foo().bar()
      

      【讨论】:

      • 为什么在没有导入的情况下允许new FileInputStream('Test.groovy').getChannel()
      • @AlexanderSuraphel “只要一切都在类路径中”
      【解决方案3】:

      根据这个pagedef 是类型名称的替代品,可以简单地认为是Object 的别名(即表示您不关心类型)。

      【讨论】:

        【解决方案4】:

        就这个单一的脚本而言,没有实际区别。

        但是,使用关键字“def”定义的变量被视为局部变量,即对这个脚本来说是局部变量。前面没有“def”的变量在首次使用时存储在所谓的绑定中。您可以将绑定视为需要在“脚本之间”可用的变量和闭包的通用存储区域。

        因此,如果您有两个脚本并使用相同的 GroovyShell 执行它们,则第二个脚本将能够获取在第一个脚本中设置的所有变量,而无需“def”。

        【讨论】:

          【解决方案5】:

          “def”的原因是告诉 groovy 你打算在这里创建一个变量。这很重要,因为您永远不想意外创建变量。

          这在脚本中是可以接受的(Groovy 脚本和 groovysh 允许您这样做),但在生产代码中这是您可能遇到的最大弊端之一,这就是为什么您必须在所有实际 groovy 代码中使用 def 定义变量(类内的任何东西)。

          这是一个为什么不好的例子。如果您复制以下代码并将其粘贴到 groovysh 中,这将运行(不会使断言失败):

          bill = 7
          bi1l = bill + 3
          assert bill == 7
          

          这类问题可能需要花费大量时间来查找和解决——即使它在你的一生中只咬过你一次,它仍然比在你的职业生涯中明确声明变量数千次要花费更多的时间。它的声明位置也一目了然,您无需猜测。

          在不重要的脚本/控制台输入(如 groovy 控制台)中,这是可以接受的,因为脚本的范围是有限的。我认为 groovy 允许您在脚本中执行此操作的唯一原因是像 Ruby 一样支持 DSL(如果您问我,这是一个糟糕的权衡,但有些人喜欢 DSL)

          【讨论】:

            【解决方案6】:

            实际上,我认为它的行为会相同......

            Groovy 中的变量仍然需要声明,只是不需要 TYPED 声明,因为右侧通常包含足够的信息供 Groovy 键入变量。

            当我尝试使用我没有用 def 或类型声明的变量时,我收到一个错误“没有这样的属性”,因为它假定我正在使用包含代码的类的成员。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2017-05-15
              • 2021-04-19
              • 2012-11-02
              • 2019-08-08
              • 1970-01-01
              • 1970-01-01
              • 2010-11-05
              相关资源
              最近更新 更多