0

以下是一个动态调用getXXX的对象上的所有方法的程序CLASS,其中CLASS-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")) {
                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
4

1 回答 1

2

之所以会发生这种情况,是因为当您使用基于反射的一个(the x.class.methods.eachone)时,您在 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:-)。

于 2013-06-02T13:42:05.590 回答