Reputation: 187399
By default every Groovy class has a Map constructor, e.g.
class Foo {
def a
def b
}
// this works
new Foo(a: '1', b: '2')
However, it seems that as soon as you add a constructor of your own, this default constructor is not available
class Foo {
Foo(Integer x) {
println 'my constructor was called'
}
def a
def b
}
// this works
new Foo(1)
// now this doesn't work, I get the error: groovy.lang.GroovyRuntimeException:
// failed to invoke constructor
new Foo(a: '1', b: '2')
Is it possible to add your own constructor without losing the default map constructor? I tried annotating the class with @TupleConstructor
but it made no difference. I realise I could add the map constructor myself, e.g.
public Foo(Map map) {
map?.each { k, v -> this[k] = v }
}
Though the constructor above is not identical to the default map constructor because a key in the map that does not have a corresponding property in the class will cause an exception.
Upvotes: 17
Views: 12586
Reputation: 5883
If you are on Groovy 2.5 or later, you can apply the @MapConstructor
annotation.
@groovy.transform.MapConstructor
class Foo {
def a, b
Foo(Integer x) {
println 'my constructor was called'
}
}
// this works
new Foo(1)
// the map constructor is present, too
def mappedFoo = new Foo(a: '1', b: '1')
assert mappedFoo.a == '1'
assert mappedFoo.b == '1'
If you're using an older version of Groovy, the @InheritConstructors
annotation can be used as a substitute for @MapConstructor
, but as Arie pointed out, avoid this approach if you're class extends some base class; if the base class lacks a no-arg constructor this will not work.
Upvotes: 22
Reputation: 14559
Upon compilation, Groovy's map constructor gets translated to an object creation using an empty constructor plus a bunch of setters (in a javabean style). Having an empty constructor solves the issue:
class Foo {
Foo(Integer x) {
println 'my constructor was called'
}
Foo() {}
def a
def b
}
new Foo(1)
foo = new Foo(a: '1', b: '2')
assert foo.a == "1"
Upvotes: 13
Reputation: 160311
Add a no-arg ctor and call super
, e.g.,
class Foo {
Foo(Integer x) {
println 'my constructor was called'
}
Foo() { super() } // Or just Foo() {}
def a
def b
}
f = new Foo(a: '1', b: '2')
println f.a
=> 1
Upvotes: 7