Groovy:动态方法调用的奇怪错误

时间:2013-06-02 07:48:08

标签: dynamic methods groovy invocation

以下是一个程序,它动态调用getXXX对象上的所有CLASS方法,其中CLASS - 名称通过命令行传递。 它运作得很好。

// Program: callAllMethods.groovy

// Invoke this program as: groovy callAllMethods Date 

args.each { arg ->
    println "Methods of ${arg} ..."

    def code = """
        x = new ${arg}()
        x.class.methods.each { f ->
            if (f.name.startsWith("get")) {
                print "new ${arg}()." + f.name + ": " + f.invoke(x) 
                println ''

            }
        }  
    """
    evaluate("$code")
    println ''
}

然而,当我尝试更简单的动态方法调用方式(不使用METHOD.invoke(OBJECT)而是使用OBJECT."METHOD-NAME"())时,就像这样,

// Program: callAllMethods.groovy

// Invoke this program as: groovy callAllMethods Date 

args.each { arg ->
    println "Methods of ${arg} ..."

    def code = """
        x = new ${arg}()
        x.class.methods.each { f ->
            if (f.name.startsWith("get")) {
                result = x."${f.name}"()
                println "new ${arg}().${f.name}: ${result}" 
            }
        }  
    """
    evaluate("$code")
    println ''
}

...我收到以下错误:

$ groovy callGetMethods.groovy Date
Methods of Date ...
Caught: groovy.lang.MissingPropertyException: No such property: f for class: callGetMethods
groovy.lang.MissingPropertyException: No such property: f for class: callGetMethods
    at callGetMethods$_run_closure1.doCall(callGetMethods.groovy:13)
    at callGetMethods.run(callGetMethods.groovy:10)

我无法理解为什么!我正在使用的Groovy版本:

$ groovy -version
Groovy Version: 2.1.3 JVM: 1.6.0_43 Vendor: Sun Microsystems Inc. OS: Linux

1 个答案:

答案 0 :(得分:2)

之所以会发生这种情况,是因为当你使用基于反射的(x.class.methods.each一个)时,你会连接并在GString评估时生成代码,它只针对当前范围解析一个变量,即arg ,那没关系。如果您打印代码,它会输出一个完全可运行的Groovy代码:

x = new Date()
x.class.methods.each { f ->
    if (f.name.startsWith("get")) {
        print "new Date()." + f.name + ": " + f.invoke(x) 
        println ''
    }
}  

在第二个版本中,GString变量将根据它们创建的范围进行解析,即脚本绑定。因此它尝试从该范围中获取f变量,而不是从code变量中获取。这就是它在${f}变量崩溃的原因。

如果您将code变量更改为普通字符串(单引号),它将无法解析变量arg,因此您需要对其进行一些调整以创建新变量从它上课。即便如此,它也会失败,除非你作为参数groovy callAllMethods java.util.Date传递,这不是常规(双关语)。

因此,要以这种方式使用您的代码,GString不应该在声明时间内解析,而是在evaluate()时间解析。仍然,arg变量需要在声明时间内解析,因此,您需要连接它。结果如下:

args.each { arg ->
    println "Methods of ${arg} ..."

    def code = '''
        x = new '''+arg+'''()
        x.class.methods.each { m ->
            if (m.name.startsWith("get")) {
                result = x."${m.name}"()
                println "new '''+arg+'''().${m.name}: ${result}"
            }
        }  
    '''
    evaluate code
    println ''
}

其中,在我的方框(jdk7,groovy 2.1.3)中,输出:

new Date().getDay: 0
new Date().getTimezoneOffset: 180
new Date().getDate: 2
new Date().getHours: 10
new Date().getMinutes: 39
new Date().getMonth: 5
new Date().getSeconds: 56
new Date().getTime: 1370180396136
new Date().getYear: 113
new Date().getClass: class java.util.Date

如果您只想从对象输出属性,我可以建议object.properties吗?

args.each { arg ->
    println "Methods of ${arg} ..."

    def code = '''
        x = new '''+arg+'''()
        x.properties.each { 
            println "new '''+arg+'''().${it.key}: ${x[it.key]}"
        }  
    '''
    evaluate code
    println ''
}

它为Date输出了更多东西,但是: - )。