2015-04-14 2 views
6

В заводной можно легко создавать объекты карты и автоматически заполнить соответствующие поля:Обновление заводной полей объекта с карты

def myAddress = new Address([street:"King's street", number:"1200"]) 

Можно также обновить существующий объект с карты, не воссоздавать его? Что-то вроде ...

myAddress.update([zip: "30555050", city: "London"]) 
+0

в bad 'myAddress.properties.putAll' не работает – cfrick

ответ

7

Вы можете использовать object."${variable}" аксессоров сделать это:

map.each { key, value -> 
    object."${key}" = value 
} 

Вы можете создать метод, который делает это, и установить, что на Object.metaClass и он будет доступен везде:

@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 
    } 
} 
+0

Хороший @Raniz. Хотя мне потребуется некоторое время, чтобы найти лучшее место для «установки» этого поведения в моем приложении Grails. Может быть, на bootstrap.groovy. Любое предложение? –

+2

Работает как рассеянное. И реализация этого на boostrap.groovy (для приложения Grails), похоже, выполняет эту работу. Благодарю. –

+0

Я не разработчик Grails, поэтому не могу вам помочь. – Raniz

3

Вы можете использовать InvokeHelper категории и setProperties метод, вот короткий пример:

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' 

Хотя, если вы можете избежать изменяемые объекты, это лучше для вас , В противном случае вам нужно подумать о безопасности потоков, чтобы не столкнуться с проблемами параллелизма. Вы можете использовать предыдущий пример для создания статического метода, который ожидает 2 аргумента: существующий объект и карту свойств для обновления. В результате вы получаете новый экземпляр, содержащий обновленные поля. Также вы можете сделать свой класс неизменным.

+0

Спасибо @Szymon Stepniak. Я попробую ваше решение. Но должен ли я действительно беспокоиться о безопасности потоков, если объект Address создается локально и используется только, скажем, во время HTTP-запроса? Или вы просто предупреждаете меня, если моя цель получена из некоторой базовой структуры (например, hibernate) и, таким образом, potentilaly, разделяемой другими потоками? –

+1

Это зависит от варианта использования - если вы начинаете делиться своим объектом между несколькими потоками, вы можете ** запускать проблемы параллелизма. Если вы используете его только как локальную переменную, вам не о чем беспокоиться. Просто имейте это в виду, стоит подумать об этом :-) –

Смежные вопросы