Reputation: 1016
In groovy we can easily create objects from maps and fill the corresponding fields automatically:
def myAddress = new Address([street:"King's street", number:"1200"])
Is it possible to also update an existing object from a map without recreating it? Something like...
myAddress.update([zip: "30555050", city: "London"])
Upvotes: 15
Views: 9410
Reputation: 62789
After looking up/learning from Szymon's Excellent answer and finding a different way to invoke the helper, it seems like the answer can be simplified to:
InvokerHelper.setProperties(myAddress, [zip: "30555050", city: "London"])"
which is amazingly close to your requested
myAddress.update([zip: "30555050", city: "London"])
I added this as a comment to his question but it's so easy I thought it deserved a terse top-level answer of it's own.
Upvotes: 7
Reputation: 11123
You can use object."${variable}"
accessors to do this:
map.each { key, value ->
object."${key}" = value
}
You can then create a method that does this and install that on Object.metaClass and it will be available everywhere:
@Canonical
class MapSet {
String name
int count
static def setAttributesFromMap(Object o, Map<String, Object> map) {
map.each { key, value ->
o."${key}" = value
}
}
static void main(String[] args) {
Object.metaClass.update = {
setAttributesFromMap delegate, it
}
def o = new MapSet([
name: "foo",
count: 5
])
assert o.name == "foo"
assert o.count == 5
o.update([
name: "bar",
count: 6
])
assert o.name == "bar"
assert o.count == 6
}
}
Upvotes: 17
Reputation: 42262
You can use InvokeHelper category and setProperties method, here is a short example:
import groovy.transform.EqualsAndHashCode
import groovy.transform.ToString
import org.codehaus.groovy.runtime.InvokerHelper
@EqualsAndHashCode
@ToString
class Address {
String street
String number
String city
}
Address mainAddress = new Address(street: 'Test', number: '2B', city: 'London')
use InvokerHelper, {
mainAddress.setProperties([street: 'Lorem', number: 'Ipsum'])
}
assert mainAddress.street == 'Lorem'
assert mainAddress.number == 'Ipsum'
assert mainAddress.city == 'London'
Although if you can avoid mutable objects, it's better for you. Otherwise you have to think about thread-safety to do not run into concurrency problems. You can use previous example to create a static method that expects 2 arguments: the existing object and a map of properties to update. In result you get a new instance that contains updated fields. Also you can make your class an immutable one.
Upvotes: 6