Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/google-apps-script/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
可调用对象的GroovyShell和Binding.setVariable()_Groovy - Fatal编程技术网

可调用对象的GroovyShell和Binding.setVariable()

可调用对象的GroovyShell和Binding.setVariable(),groovy,Groovy,我正在用GroovyShell进行一些动态变量创建的实验,遇到了一个问题。首先,工作守则: static def defVar(def glob) { glob.setVariable('test', new Test()) } class MyBinding extends Binding { } class Test { def call() { println("--- hello ---") } } Binding glob = new My

我正在用
GroovyShell
进行一些动态变量创建的实验,遇到了一个问题。首先,工作守则:

static def defVar(def glob) {
    glob.setVariable('test', new Test())
}

class MyBinding extends Binding {
}

class Test {
    def call() {
        println("--- hello ---")
    }
}

Binding glob = new MyBinding()
GroovyShell shell = new GroovyShell(glob)
defVar(glob)
shell.parse('test()').run()
这给了我预期的输出:

--- hello ---
但是,我想在调用
getVariable()
时动态调用
setVariable()
,如下所示:

static def defVar(def glob) {
    glob.setVariable('test', new Test())
}

class MyBinding extends Binding {
    def getVariable(String name) {
        if (! hasVariable('test')) {
            BindingTest.defVar(this)
        }
        return super.getVariable(name)
    }
}

class Test {
    def call() {
        println("--- hello ---")
    }
}

Binding glob = new MyBinding()
GroovyShell shell = new GroovyShell(glob)
//defVar(glob)
shell.parse('test()').run()
class MyBinding extends Binding {
    def getVariable(String name) {
        if (! hasVariable(name)) {
            BindingTest.defVar(this)
        }
        println("getVariable: ${name}: ${super.getVariable(name).getClass().getName()}")
        return super.getVariable(name)
    }

    void setVariable (String name, def val) {
        println("setVariable: ${name}: ${val.getClass().getName()}")
        super.setVariable(name, val)
    }

    def getProperty(String name) {
        println("getProperty: ${name}: ${super.getProperty(name)}")
        return super.getProperty(name)
    }

    void setProperty (String name, def val) {
        println("setProperty: ${name}: ${val.getClass().getName()}")
        super.setProperty(name, val)
    }
}
但此操作失败,错误如下:

Caught: groovy.lang.MissingMethodException: No signature of method: Script1.test() is applicable for argument types: () values: []
Possible solutions: getAt(java.lang.String), use([Ljava.lang.Object;), use(java.lang.Class, groovy.lang.Closure), use(java.util.List, groovy.lang.Closure), wait(), wait(long)
groovy.lang.MissingMethodException: No signature of method: Script1.test() is applicable for argument types: () values: []
Possible solutions: getAt(java.lang.String), use([Ljava.lang.Object;), use(java.lang.Class, groovy.lang.Closure), use(java.util.List, groovy.lang.Closure), wait(), wait(long)
    at Script1.run(Script1.groovy:1)
    at Script1$run.call(Unknown Source)
    at BindingTest.run(BindingTest.groovy:23)
当我添加如下跟踪代码时:

static def defVar(def glob) {
    glob.setVariable('test', new Test())
}

class MyBinding extends Binding {
    def getVariable(String name) {
        if (! hasVariable('test')) {
            BindingTest.defVar(this)
        }
        return super.getVariable(name)
    }
}

class Test {
    def call() {
        println("--- hello ---")
    }
}

Binding glob = new MyBinding()
GroovyShell shell = new GroovyShell(glob)
//defVar(glob)
shell.parse('test()').run()
class MyBinding extends Binding {
    def getVariable(String name) {
        if (! hasVariable(name)) {
            BindingTest.defVar(this)
        }
        println("getVariable: ${name}: ${super.getVariable(name).getClass().getName()}")
        return super.getVariable(name)
    }

    void setVariable (String name, def val) {
        println("setVariable: ${name}: ${val.getClass().getName()}")
        super.setVariable(name, val)
    }

    def getProperty(String name) {
        println("getProperty: ${name}: ${super.getProperty(name)}")
        return super.getProperty(name)
    }

    void setProperty (String name, def val) {
        println("setProperty: ${name}: ${val.getClass().getName()}")
        super.setProperty(name, val)
    }
}
在工作情况下,我得到以下输出:

setVariable: test: Test
--- hello ---
在非工作情况下,我得到以下输出:

setVariable: test: Test
getVariable: test: Test
Caught: groovy.lang.MissingMethodException: No signature of method: Script1.test() is applicable for argument types: () values: []
...
两个问题:

  • 在工作场景中,为什么没有
    getVariable
  • 在非工作场景中,为什么
    getVariable
    返回的
    Test
    对象被拒绝
  • 请注意,此问题特定于可调用值。如果我将一个简单的值(如字符串)设置为
    test
    ,那么这两种方法都可以正常工作。例如,这种变化:

    ...
    static def defVar(def glob) {
        glob.setVariable('test', '--- hello ---')
    }
    ...
    shell.parse('println(test)').run()
    
    我使用这两种方法得到以下相同的输出:

    setVariable: test: java.lang.String
    getVariable: test: java.lang.String
    setVariable: test: java.lang.String
    --- hello ---
    
    不过,我不知道为什么
    setVariable
    会被调用两次。我找不到任何文件来解释这些令人费解的行为。这里有人能解释一下吗


    请注意,所有代码片段都经过了简化,以便于演示问题,而不是为了达到其预期目的

    当您使用属性作为可调用的后备方法时,
    Binding.getVariable()
    方法不涉及。此行为由元类控制,在您的例子中,它都会驱动执行
    MetaClassImpl.invokePropertyOrming()
    方法。此方法确定

    test()
    
    应该调用
    test.call()
    (对于现有属性),或者应该回退到
    missingMethod()
    方法。以下是此方法实现的外观:

    private Object invokePropertyForming(对象对象对象、字符串方法名、对象[]原始参数、boolean fromInsideClass、boolean isCallToSuper){
    //如果找不到方法,请尝试查找定义为类字段的闭包并运行它
    对象值=空;
    final MetaProperty MetaProperty=this.getMetaProperty(methodName,false);
    if(元属性!=null)
    value=metaProperty.getProperty(对象);
    否则{
    if(映射的对象实例)
    value=((映射)对象).get(方法名);
    }
    if(value instanceof Closure){//此测试确保值!=此如果您更改此确保值!=此
    闭包=(闭包)值;
    元类delegateMetaClass=closure.getMetaClass();
    返回delegateMetaClass.invokeMethod(closure.getClass(),closure,closure\u DO\u CALL\u方法,originalArguments,false,fromInsidelClass);
    }
    if(脚本的对象实例){
    对象bindingVar=((脚本)对象).getBinding().getVariables().get(方法名);
    if(bindingVar!=null){
    元类bindingVarMC=((MetaClassRegistryImpl)注册表);
    返回bindingVarMC.invokeMethod(bindingVar、闭包调用方法、原始参数);
    }
    }
    返回invokeMissingMethod(对象、方法名、原始参数、null、isCallToSuper);
    }
    
    资料来源:

    现在,请注意分支
    if(脚本的对象实例)
    以及如何检索绑定变量。它尝试使用以下方法从绑定对象检索
    test
    变量:

    objectbindingvar=((脚本)对象).getBinding().getVariables().get(方法名);
    
    如果您的代码是:

    objectbindingvar=((脚本)对象).getBinding().getVariable(方法名);
    
    相反。但事实并非如此

    如果重写
    getVariables()
    方法而不是
    getVariable(字符串名称)
    ,则可以使第二个案例正常工作,例如:

    class MyBinding extends Binding {
        @Override
        Map getVariables() {
            return super.getVariables() + [
                    test: new Test()
            ]
        }
    }
    
    当然,您的最终实现可能要复杂得多。(例如,您可以先获取
    super.getVariables()
    map,检查缺少哪些变量,并仅在给定变量缺少初始映射时添加默认变量。)但这取决于您自己


    可选地,考虑使用<代码>方法> 而不是绑定变量后退。它可以使您的代码更易于阅读和推理。

    Szymon,无法理解为什么
    shell.parse('println“smth”\n test()')。run()
    在第一个非工作模式下工作example@daggett当您添加
    println
    时,它会工作,因为
    groovy.lang.Script
    类中的
    println
    方法调用
    getProperty(“out”)
    ,在本例中,它将
    test
    添加到绑定变量中。(在本例中,任何调用
    getProperty()
    getVariable()
    方法都会初始化绑定变量中的默认值。)这是一个相关的源代码:感谢您提供了指向底层魔术的指针。有了您提供的信息,我能够使用重写的
    get()
    构建一个自定义
    Map
    ,用于检测缺少的变量并动态设置它,效果很好。