问题
I am trying to use call operator ()
overloading, but it does not work on class fields. What's wrong?
class Foo {
void call(int x){
println("x="+x)
}
}
class MyCallable {
Foo foo = new Foo()
}
Foo foo = new Foo()
foo(5) //works
MyCallable mc = new MyCallable()
mc.foo(2) //not works
But program terminated with exception:
Exception in thread "main" groovy.lang.MissingMethodException: No
signature of method: mpctests.MyCallable.foo() is applicable for
argument types: (java.lang.Integer) values: [2]
回答1:
You get MissingMethodException
when you call mc.foo(5)
, because Groovy's invoke object method mechanism gets triggered. There is one thing worth explaining to get a better understanding about this situation. Your MyCallable
class:
class MyCallable {
Foo foo = new Foo()
}
gets compiled to something like this:
import groovy.lang.GroovyObject;
import groovy.lang.MetaClass;
import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
import org.codehaus.groovy.runtime.callsite.CallSite;
public class MyCallable implements GroovyObject {
private Foo foo;
public MyCallable() {
CallSite[] var1 = $getCallSiteArray();
Object var2 = var1[0].callConstructor(Foo.class);
this.foo = (Foo)ScriptBytecodeAdapter.castToType(var2, Foo.class);
MetaClass var3 = this.$getStaticMetaClass();
this.metaClass = var3;
}
public Foo getFoo() {
return this.foo;
}
public void setFoo(Foo var1) {
this.foo = var1;
}
}
Groovy also compiles every field access like mc.foo
to a getter method call mc.getFoo()
. So when you call mc.foo(5)
it is clear for Groovy runtime that you expect to call a foo(5)
method on mc
object. And this method does not exist and MissingMethodException
gets thrown.
However, it works if you create object def foo = new Foo()
and then you call foo(5)
, because foo
is an object and foo(5)
is a strict instruction to invoke call(5)
method on foo
object (foo(5)
is a shorthand version of foo.call(5)
). The same situation would take place if you call mc()
- Groovy would try to invoke mc.call()
method. But when you say mc.foo(5)
it's clear that you are trying to invoke foo(5)
method.
If you want to use call operator on mc.foo
field there are two options:
1. Use direct field access operator @
mc.@foo(5)
In this case you refer directly to foo
field and you can use shorthand call operator.
2. Use with {}
method
mc.with {
foo(5)
}
In this case it is also a straightforward for Groovy runtime that you are accessing foo
field and you can use call operator on it.
Alternatives
Using getter method:
mc.getFoo()(5)
Using method call()
directly:
mc.foo.call(5) // equivalent of mc.getFoo().call(5)
来源:https://stackoverflow.com/questions/51727088/groovy-call-operator-throws-missingmethodexception-when-used-on-a-class-field