I have a simple script, which runs and works:
println testReturn()
String testReturn() {
def str = /asdf/
return str
}
If I change it to this, however, I see an error when I run it:
println testReturn()
String testReturn() {
return /asdf/
}
Error:
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
test.groovy: 6: unexpected token: / # line 6, column 12.
return /asdf/
^
1 error
Does anyone know why I have to define a slash-escaped string before returning it?
It seems to me that the return statement is treated like other function calls in groovy 2.x and that slashy strings do not work well with paren-less function calls.
An example:
println /asdf/ // fails
println(/asdf/) // works
println testReturn()
String testReturn() {
/asdf/ //works - implicit return
}
String btestReturn() {
return /asdf/ // fails
}
String testReturn() {
return(/asdf/) //works
}
in my book this is unexpected behavior and a restriction of the groovy parser.
In groovy 3 (or more specifically in this case, groovy 3.0.0-alpha-3), the parser has been rewritten and the following code:
println /asdf/ // fails
println(/asdf/) // works
println testReturn1()
println testReturn2()
println testReturn3()
String testReturn1() {
/asdf/ // works
}
String testReturn2() {
return /asdf/ // works
}
String testReturn3() {
return(/asdf/) // works
}
gives the somewhat more informative error:
Caught: groovy.lang.MissingPropertyException: No such property: println for class: solution
groovy.lang.MissingPropertyException: No such property: println for class: solution
at solution.run(solution.groovy:1)
which tells us that without parens, groovy can not determine if println is a property or a method call. Removing that first line makes the above code work under groovy 3.x.
Related
I have a Java app through which I'm executing a Groovy Script, the problem occurs when my Script again has code to use the groovy shell to execute the Inner script.
something like this
import com.xx.WrappedGroovyShell
import java.lang.*
def scriptString = """
def test(str) {
str.toLowerCase() // This doesn't work as GString can't seem to be treated as a String
}
"""
try {
def script = WrappedGroovyShell.getInstance().getScript("Test1", scriptString)
def script2 = new GroovyShell().parse(scriptString)
def example = 1
def gstring = "OUR VALUE IS ${example}";
println script instanceof GroovyObject // Statement returning false
println script.test(gstring) // This throws an exception groovy.lang.MissingMethodException: No signature of method: org.codehaus.groovy.runtime.GStringImpl.toLowerCase() is applicable for argument types: () values: []
println script2 instanceof GroovyObject // Statement returns true
println script2.test(gstring) //this one works
println "Success"
} catch (ex) {
println ex
}
return 0
The getScript method in WrappedGroovyShell.java is as simple as
public Script getScript(String scriptName, String scriptBody){
return new GroovyShell().parse(scriptBody);
}
I will be thankful if someone let me know why two different behavior.
new GroovyShell() creates a new groovy classloader with parent = GroovyShell.class.getClassLoader()
and if GString.class is unknown for parent classloader - then it will be loaded again.
two GString classes loaded by different classloaders are not equal that's why you could have unexpected exceptions like "method not found for parameter GString" or "can't cast GString to GString"
this code should work because it uses classloader of current groovy class
def scriptText='''
def test(str) {
str.toLowerCase()
}
'''
def gs = new GroovyShell(this.getClass().getClassLoader())
def script = gs.parse(scriptText)
def arg = "GSTRING ${123}"
println script.test(arg)
I have a groovy file with the following 3 statements:
def mySillClosure = { null }
def returnValue = mySillClosure.call()
println returnValue
This prints
null
as expected.
If I modify the file and add a closure definition at the end as follows:
def mySillClosure = { null }
def returnValue = mySillClosure.call()
println returnValue
{ String foo -> foo.toLowerCase() }
I am starting to get:
Caught: java.lang.NullPointerException: Cannot invoke method call() on null object
java.lang.NullPointerException: Cannot invoke method call() on null object
at mygroovy.run(mygroovy.groovy:3)
How does defining a closure changing the behaviour for a previous line?
BTW, if I only have the following only in the file, it will run fine - but will not print anything:
{ String foo -> foo.toLowerCase() }
It looks like returnValue is treated as a method that accepts a closure. In groovy if you omit parentheses ( the last argument is the only argument in this case - its a closure), it will treat this closure like a method argument:
println returnValue { String foo -> foo.toLowerCase() }
is Basically the same as:
println returnValue({String foo -> foo.toLowerCase()})
// its a method that accepts a closure as a parameter
But as you said, the method is null by itself, so it can't really call the closure.
For example the following code will do the job:
def method(Closure s) {
s.call("HELLO")
}
println method { String foo -> foo.toLowerCase() }
// or, just the same:
println method({ String foo -> foo.toLowerCase() })
Both will print a lower-cased: hello
I am trying to make my own Dsl and was playing around with different styles of closures in groovy.
I've stumbled upon follwing snippet:
myClosure {
testProperty: "hello!"
}
But can't figure out if this a valid code and how can I access then this testProperty. Is it valid? How can I read "hello!" value?
For now, lets put aside closures, consider the following code:
def f1() {
testProperty : 5
}
def f2() {
testProperty : "Hello"
}
println f1()
println f1().getClass()
println f2()
println f2().getClass()
This compiles (therefor the syntax is valid) and prints:
5
class java.lang.Integer
Hello
class java.lang.String
So what you see here is just a labeled statement (groovy supports labels see here)
And bottom line the code of f1 (just like f2) is:
def f1() {
return 5 // and return is optional, so we don't have to write it
}
With closures its just the same from this standpoint:
def method(Closure c) {
def result = c.call()
println result
println result.getClass()
}
method {
test : "hello"
}
This prints
hello
class java.lang.String
as expected
Usually in DSL you have either this:
mySomething {
a = 42
b = 84
}
which corresponds to property setting
or this:
mySomething( a:42, b:84 ){
somethingElse{}
}
which is a method call with Map-literal.
The code you shown is not used as #mark-bramnik explained.
With groovy .& operator one can create references to static methods, as in
def static xyz( name='Joe' ) {
println "Hello ${name}"
}
// NOTE: ConsoleScript number part varies
def ref = ConsoleScript52.&xyz
And can be easilly called without params, as
ref() // prints "Hello "
But how can this method be called with params? ref('John') gives an error groovy.lang.MissingMethodException: No signature of method: ConsoleScript52.xyz() is applicable for argument types: (java.lang.String) values: [John]
Note that Groovy does not even use the default value of name param in above example when static method is called with ref.
You are probably using a ConsoleScript instance where you did not define that method with parameters.
In the code below, in the 8th execution in my console (ConsoleScript8) y defined the method hello() without parameters (added the "script number" to make it explicit):
println this.getClass().getName()
println ''
def static hello() {
println "(8) Hello"
}
def h8 = ConsoleScript8.&hello
h8()
h8('Joe')
And it yields the following in the next execution:
ConsoleScript9
(8) Hello
Exception thrown
groovy.lang.MissingMethodException: No signature of method: ConsoleScript9.hello() is applicable for argument types: (java.lang.String) values: [Joe]
And in the 10th execution (ConsoleScript10) I modified it adding the default parameter:
println this.getClass().getName()
println ''
def static hello(name='amigo') {
println "(10) Hello ${name}"
}
def h10 = ConsoleScript10.&hello
h10()
h10('Joe')
println '--'
def h8 = ConsoleScript8.&hello
h8()
h8('Joe')
And it yields:
ConsoleScript12
(10) Hello amigo
(10) Hello Joe
--
(8) Hello
Exception thrown
groovy.lang.MissingMethodException: No signature of method: ConsoleScript8.hello() is applicable for argument types: (java.lang.String) values: [Joe]
It is easier if you use an explicit class:
class Greeter {
def static hello(name='Joe') {
"Hello ${name}"
}
}
def hi = Greeter.&hello
assert hi() == 'Hello Joe'
assert hi('Tom') == 'Hello Tom'
What version of Groovy? This works with Groovy 2.4.5:
class Test {
static greet(name = 'tim') {
"Hello ${name.capitalize()}"
}
}
// regular static calls
assert Test.greet() == 'Hello Tim'
assert Test.greet('kaskelotti') == 'Hello Kaskelotti'
// Create a reference to the static method
def ref = Test.&greet
// Calling the reference works as well
assert ref() == 'Hello Tim'
assert ref('kaskelotti') == 'Hello Kaskelotti'
I'm trying to understand what is happening when I get errors like "groovy.lang.MissingMethodException: No signature of method: Three.method() is applicable for argument types: "
b = "Tea"
class Three
{
String myVar1, myVar2, myVar3, myVar4, myVar5, myVar6
def method(myVar1,myVar2,myVar3,myVar4,myVar5,myVar6)
{
try {
println myVar1, myVar2, myVar3, myVar4, myVar5, myVar6
} catch (groovy.lang.MissingPropertyException e) {
println "error caught"
}
}
}
try {
new Three().method(myVar1:b);
} catch (groovy.lang.MissingPropertyException e) {
println "error caught"
}
try {
new Three().method(myVar1=b);
} catch (groovy.lang.MissingPropertyException e) {
println "error caught"
}
try {
new Three().method(b);
} catch (groovy.lang.MissingPropertyException e) {
println "error caught"
}
I think that you're mixing different concepts... by default groovy classes has two default constructor, the default with no params and the map based constructor which works as follows:
def three = new Three(myVar1:'a',myVar2:'b',myVar3:'c')
println three.myVar1 // prints a
println three.myVar2 // prints b
println three.myVar3 // prints c
However in the case of the methods there isn't this default behavior and due you can not use this kind of invocation and you've to fit the signature of the method, in your case the method expects 6 arguments and you're trying to invoke it passing a map this is why you're getting a missingMethodException, because there is no method with this signature inside your class.
In your case you only have one method method() with 6 parameters with not implicit type so you've to invoke it like this:
three.method(1,'kdk','asasd',2323,'rrr',['a','b'])
// or like this
three.method(1,'kdk','asasd',2323,'rrr','b')
// or like this
three.method(1,2,3,4,5,6)
// ...
Note that in your code there is another error, you are invoking println erroneously inside method()... use this:
println "$myVar1 $myVar2 $myVar3 $myVar4 $myVar5 $myVar6"
instead of:
println myVar1, myVar2, myVar3, myVar4, myVar5, myVar6
Hope this helps,