I know that this is what closures are for, but shouldn't the below work as well?:
def f = 'foo'
def foo() {
println(f)
}
foo()
It results in:
Caught: groovy.lang.MissingPropertyException: No such property: f for class: bar
groovy.lang.MissingPropertyException: No such property: f for class: bar
at bar.foo(bar.groovy:4)
at bar.run(bar.groovy:7)
In a groovy script (as opposed to class), your code is essentially equivalent to:
class ScriptName {
def main(args) {
new ScriptName().run(args)
}
def run(args) {
def f = 'foo'
foo()
}
def foo() {
println(f)
}
}
the 'implicit' enclosing class created by groovy for groovy scripts is always present but not visible in your code. The above makes it obvious why the foo method does not see the f variable.
You have a couple of options
option 1 - binding
See the groovy docs on script bindings for details.
// put the variable in the script binding
f = 'foo'
this is shorthand for:
binding.setVariable('f', 'foo')
where the script binding is visible everywhere for groovy scripts and this makes the variable essentially 'global'.
option 2 - #Field annotation
See groovy docs on the Field annotation for details.
import groovy.transform.Field
...
// use the groovy.transform.Field annotation
#Field f = 'foo'
the Field annotation is specifically there to give groovy scripts the ability to add fields to the 'implicit' enclosing class. The generated class would then look something like the following:
class ScriptName {
def f = 'foo'
def main(args) {
new ScriptName().run(args)
}
def run(args) {
foo()
}
def foo() {
println(f)
}
}
which also essentially makes the variable accessible 'globally' in the script.
Related
I'm running the following code (in Jenkins script console):
def sayHello() {
println "Hello"
}
class MyClass {
MyClass() {
sayHello()
}
}
def a = new MyClass()
In all the good faith, I expect the constructor code to call the function that will print Hello.
Instead I get
groovy.lang.MissingMethodException: No signature of method: MyClass.sayHello() is applicable for argument types: () values: []
at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.unwrap(ScriptBytecodeAdapter.java:58)
What is going on here? I can't call the function from inside the class?
You get the error because you cannot access methods of one class from another class, unless you access an instance of that class.
In your case, the code is embedded automatically into a run() method inside the Main class derived from groovy.lang.Script. The class MyClass is an inner class of the Main class. See here Scripts versus classes.
Solution: to access the method sayHello() of the Main class, you must pass an instance of it, using this keyword:
def sayHello() {
println "Hello"
}
class MyClass {
MyClass(Script host) {
host.sayHello()
}
}
def a = new MyClass(this)
Not sure what and why you are trying to do, but the simplest option to call a "function" from a constructor inside a Script is to put it in another class:
class A {
static sayHello() {
println "Hello"
}
}
class MyClass {
MyClass() {
A.sayHello()
}
}
def a = new MyClass()
I tried some (runtime) metaprogramming with Groovy. I went ahead and implemented the getProperty method of GroovyInterceptable. But now I found out that this only works when getting a property on the object from the outside. When getting an property from inside a method in that class my getProperty method does not called (see the code example below).
Now, is this expected and has it always been like this? A collegue told me this used to be different in the past (from what he remembers). Is there another way where both property reads from inside and outside would call my getProperty method?
class SomeGroovyClass implements GroovyInterceptable {
def foo
SomeGroovyClass() {
foo = "ctor"
}
def getProperty(String name) {
if (name == "foo") {
return "blub"
} else {
return "foo"
}
}
def test2() {
System.out.println "foo from inside via method call: " + this.foo
}
}
def someGroovyClass = new SomeGroovyClass()
System.out.println "foo from outside: " + someGroovyClass.foo
someGroovyClass.test2()
Output is
foo from outside: blub
foo from inside via method call: ctor
One way to force the use of the getProperty method is to force the type used to access this. Changing your test2 method to:
def test2() {
println "foo from inside via method call: " + ((GroovyInterceptable) this).foo
}
results in:
~> groovy solution.groovy
foo from outside: blub
foo from inside via method call: blub
alternatives to forcing the type:
def test2() {
def me = this as GroovyInterceptable
println "foo from inside via method call: " + me.foo
}
and
def test2() {
GroovyInterceptable me = this
println "foo from inside via method call: " + me.foo
}
I can grok where the groovy compiler is coming from...there really is no way for it to know which handling of the foo property you are looking for unless you are explicit about it.
I believe the main purpose of the getProperty mechanism is to cover access of non-existent properties. This makes defaulting to the existing property when one is available a reasonable choice in my opinion and they still leave the door open as you can always force things using typed access like in the above.
I have a question about scoping rules in Groovy. In the following snippet, I have three variables, a has local scope, b has script scope, and c should get script scope as well using the #Field annotation.
#!/usr/bin/groovy
import groovy.transform.Field;
//println org.codehaus.groovy.runtime.InvokerHelper.getVersion()
def a = 42;
b = "Tea"
#Field def c = "Cheese"
void func()
{
// println a // MissingPropertyException
println b // prints "Tea"
println c // prints "Cheese" with groovy 2.1.2, MissingPropertyException with groovy 1.8.6
}
class Main
{
def method()
{
// println a // MissingPropertyException
// println b // MissingPropertyException
// println c // MissingPropertyException with both 1.8.6. and 2.1.2
}
}
func();
new Main().method();
I get MissingPropertyExceptions on the lines indicated with comments. The exceptions on a are expected, as that variable has local scope. But I would expect b to be accessible inside method() - it isn't.
#Field doesn't do anything in groovy 1.8.6, although after upgrading it works, so I guess that is an old bug. Nevertheless, c is inaccessible inside method() with either version.
So my questions are:
Why can't I access a variable annotated with #Field inside
method()?
How can I refer to a script variable inside method()?
When you have methods or statements outside of a class declaration in a groovy script, an implicit class is created. To answer your questions:
In your example, func() can access the field c because they are both members of the implicit class. The Main class is not, so it can't.
You need to pass a reference to the script variable to method(). One way is to pass the implicitly defined binding object, through which you can access all the script scope variables.
Example:
#!/usr/bin/groovy
import groovy.transform.Field;
//println org.codehaus.groovy.runtime.InvokerHelper.getVersion()
def a = 42;
b = "Tea"
#Field def c = "Cheese"
void func()
{
// println a // MissingPropertyException
println b // prints "Tea"
println c // prints "Cheese" with groovy 2.1.2, MissingPropertyException with groovy 1.8.6
}
class Main
{
def scriptObject
def binding
def method()
{
// println a // MissingPropertyException
println binding.b
println scriptObject.c
}
}
func();
new Main(scriptObject: this, binding: binding).method();
This script and Main are generated as two separate classes inside the same file.
As Main is not an internal class of the Script class, it cannot see the java.lang.Object c field inside the script class.
You would either have to explicitly wrap this script in a class with a static main( args ) method (and an internal Main class) or you would need to pass an instance of the script class to the method like: Main.method( this )
This is the sort of thing that the above script generates:
class Script032034034 {
Object c
Script032034034() {
c = 'Cheese'
}
Object run() {
Object a = 42
b = 'Tea'
func()
new Main().method()
}
void func() {
println b
println c
}
}
class Main {
Object method() {
}
}
I found really cool that one can do:
class Foo {
String name
}
def foo = new Foo(name:"Test")
But, it only works when my file name matches the class name. If I have a file with a bunch of classes like:
class AllClassesInOneFile {
class Bar {}
class Foo {
String name
}
}
def foo = new Foo(name:"Test")
Now, it does not work anymore I get a java.lang.IllegalArgumentException: wrong number of arguments
I wonder if it is still possible to invoke named parameter argument style with scripts and nested classes.
Regards
Seems like Groovy needs explicit reference to an instance of the outer class:
class Baz {
class Bar {}
class Foo {
String name
}
}
def baz = new Baz()
def f = new Baz.Foo(baz, [name: "john doe"])
assert f.name == "john doe"
Non-static nested object can't exist without instance of the outer class.
The same in java.
Just change nested object to static.
At runtime I'm grabbing a list of method names on a class, and I want to invoke these methods. I understand how to get the first part done from here:
http://docs.codehaus.org/display/GROOVY/JN3535-Reflection
GroovyObject.methods.each{ println it.name }
What I can't seem to find information on is how to then invoke a method once I've grabbed its name.
What I want is to get here:
GroovyObject.methods.each{ GroovyObject.invokeMethod( it.name, argList) }
I can't seem to find the correct syntax. The above seems to assume I've overloaded the default invokeMethod for the GroovyObject class, which is NOT the direction I want to go.
Once you get a MetaMethod object from the metaclass, you can call invoke on it. For example:
class MyClass {
def myField = 'foo'
def myMethod(myArg) { println "$myField $myArg" }
}
test = new MyClass()
test.metaClass.methods.each { method ->
if (method.name == 'myMethod') {
method.invoke(test, 'bar')
}
}
Alternatively, you can use the name directly:
methodName = 'myMethod'
test."$methodName"('bar')
Groovy allows for dynamic method invocation as well as dynamic arguments using the spread operator:
def dynamicArgs = [1,2]
def groovy = new GroovyObject()
GroovyObject.methods.each{
groovy."$it.name"(staticArg, *dynamicArgs)
}
Reference here
Question answered here.