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.
Related
I am using Groovy to create a package that I use in ReadyApi.
In a Groovy script test step, I do the following:
class B {
String value
boolean isSomething
}
class A {
String name
B propB
public A() {
this.name = "Maydan"
}
}
def x = (A) new A().with { propB = new B(value: "Abc", isSomething: true) }
And I get the following error:
org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot cast object 'B#6c218cea' with class 'B' to class 'A'
error at line: 15
Does someone know why? It doesn't make any sense to me.
Kind regards.
PS: I would like to create an instance of class A (by using its parameterless constructor) and setting its field propB in a single statement
You need to return your A object from the .with closure. You may do it like that:
def x = (A) new A().with { propB = new B(value: "Abc", isSomething: true); return it}
but to me personally it looks a little bit odd. I would do it like that:
def x = new A(propB: new B(value: "Abc", isSomething: true))
The same effect, but more compact and readable. It doesn't require to change your A and B definitions, this "map constructor" works out of the box in Groovy, it will call your parameterless constructor and then assigns the necessary fields (propB in your case).
I want to store a list/array of Java exception classes (e.g. NullPointerException, some custom exception classes, etc.) into a Groovy list/array, but I keep running into either casting problems or MissingPropertyException:
class Foo {
Exception[] foo
List bar
def setFoo(Exception[] values) {
this.foo = values
}
def setBar(List values) {
this.bar = values
}
}
f = Foo()
f.setFoo([NullPointerException.class])
f.setBar(Arrays.asList(NullPointerException.class))
But I can't seem to get it right. Any pointer is much appreciated. Thanks!
Changes to
class Foo {
Class[] foo
List bar
def setFoo(Class[] values) {
this.foo = values
}
def setBar(List values) {
this.bar = values
}
}
def f = new Foo()
f.setFoo([NullPointerException.class] as Class[])
f.setBar(Arrays.asList(NullPointerException.class))
foo should be an array of Class rather than Exception. (Exception is the instance of Exception Class)
You need to explicitly cast list to Class array, which does the conversion from list to array for you.
It can be done with just def list = [UserClass1, UserClass2]. In groovy, even .class not required.
You code should as below, isn't this simple?
class Foo {
def bar
}
def f = new Foo(bar: [NullPointerException, RuntimeException])
assert f.bar instanceof List<Class>
assert (f.bar as Class[]) instanceof Class[]
You can quickly try it online demo
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.
I have two immutable groovy classes that have a few shared values that I'm trying to abstract to a parent class. However when I create the following, the second test case always fails. Although everything compiles correctly and no error is thrown at runtime, when I assign the parent property int he constructor, it is never set, resulting in a null value. I havent found any documentation that forbids this, but I'm wondering is this even possible? I've tried a number of configuration of Annotations and class-types (e.g. removing abstract from the parent) but nothing seems to work short of just removing the #Immutable tag altogether.
abstract class TestParent {
String parentProperty1
}
#ToString(includeNames = true)
#Immutable
class TestChild extends TestParent {
String childProperty1
String childProperty2
}
class TestCase {
#Test
void TestOne() {
TestChild testChild = new TestChild(
childProperty1: "childOne",
childProperty2: "childTwo",
parentProperty1: "parentOne"
)
assert testChild
assert testChild.parentProperty1
}
}
Based on the code for the ImmutableASTTransformation, the Map-arg constructor added by the createConstructorMapCommon method does not include a call to super(args) in the method body.
which means that immutable classes are self contained by default
Now if you want to do it you need to use composition instead of inheritance and this is an example of how you can do it :
import groovy.transform.*
#TupleConstructor
class A {
String a
}
#Immutable(knownImmutableClasses=[A])
class B {
#Delegate A base
String b
}
def b = new B(base: new A("a"), b: "b")
assert b.a
i hope this will help :)
This is just a very basic example of what I want to do. There's a bit more that goes on in the foobar method, but it's the gist of what I'm doing. It obviously doesn't work, since it fails to compile, but I'm wondering if I'm just passing the class incorrectly or using the 'className' parameter in the wrong way. I know I can rework it to take the string of the class name and just match it, but it seems a shame to do that. This would be so nice and DRY.
class Foo {
String name
}
class Bar {
String name
}
def foobar(field, className) {
def instance = className.findByName(jsonParams.field)
if(!instance) {
instance = new className(name: jsonParams.field)
}
return instance
}
foobar(foos, Foo)
foobar(bars, Bar)
I don't know much Java or Groovy, so I'm not sure what's possible vs impossible yet. Feel free to just tell me "No." I've tried googling and haven't found anything that really answers the question for me. A simple no would be great at this point haha.
Yes, it is possible to pass class as argument - in Groovy, classes are first class citizens (see this thread for more detail).
This construct: instance = new className(name: jsonParams.field) actually tries to create an instance of class named className, not of the class referenced by this variable. To make it compile, you need to call Class.newInstance:
class Foo {
String name
}
class Bar {
String name
}
def foobar(String name,Class clazz) {
def instance = clazz.findByName(name)
if(!instance) {
instance = clazz.newInstance(name:name)
}
return instance
}
foobar('foo', Foo)
foobar('bar', Bar)
I'm not entirely sure what you want to achieve with the findByName method, though - neither Foo nor Bar have a static method named findByName as far as I can tell.