2

Given an object with properties and a constructor, I wish to copy the constructor arguments into properties, and then do some additional work in the Constructor.

import groovy.transform.TupleConstructor

@TupleConstructor
class Thing{
    def one
    def two

    public Thing(one, two){
       doSomething()
    }

    def doSomething(){
        println "doing something with one : $one and two: $two"
    }
}


println new Thing(1, 2).dump()

This will successfully copy the args to the properties if I do nothing else in the constructor, but if I call "doSomething()" in the constructor, the properties are not copied.

I'm seeking "The Groovy" Way for copying args to properties.

ataylor
  • 64,891
  • 24
  • 161
  • 189
marc esher
  • 4,871
  • 3
  • 36
  • 51

2 Answers2

6

As tim_yates mentioned, the TupleConstructor AST transformation won't do anything if you have another constructor defined (you can blame this line of code =P). If you need to run some other code in the construction of the object, you may add that in a static factory method and use that instead of the tuple constructor directly:

import groovy.transform.TupleConstructor

@TupleConstructor
class Thing {
    def one
    def two

    def doSomething(){
        println "doing something with one : $one and two: $two"
    }

    static create(...args) {
        def thing = new Thing(*args)
        thing.doSomething()
        thing
    }
}


println Thing.create(1, 2).dump()

Notice that i'm using a variable-argument static method to receive an arbitrary number of parameters and then calling the tuple constructor with those parameters (used the "spread" (*) operator for that).

Unfortunately, the TupleConstructor AST transform does not seem to have an option for adding the tuple constructor as private, which would be useful in this case.

Community
  • 1
  • 1
epidemian
  • 18,817
  • 3
  • 62
  • 71
4

If you use TupleConstructor, it will not run if you have defined your own constructor.

And as you have defined a duplicate constructor to the one TupleConstructor will generate in the bytecode, even doing @TupleConstructor( force=true ) won't help you as you will just get a java.lang.ClassFormatError: Duplicate method name&signature in class file Thing

The best I can think of at the moment is to do:

class Thing{
    def one
    def two

    public Thing( Map params ){
       this.class.declaredFields.grep { !it.synthetic }.name.each { name ->
         this[ name ] = params[ name ]
       }
       doSomething()
    }

    def doSomething(){
        println "doing something with one : $one and two: $two"
    }
}


println new Thing(one:1, two:2).dump()

Though there is probably a better way that I'm missing

tim_yates
  • 167,322
  • 27
  • 342
  • 338
  • Also see [this question](http://stackoverflow.com/questions/9072307/copy-groovy-class-properties) and [this question](http://stackoverflow.com/questions/7169601/how-to-assign-all-matching-properties-from-groovy-object-to-java-object) which are pretty similar to this one... – tim_yates May 04 '12 at 15:18
  • This also won't work if Thing extends something, as it won't see the inherited properties – tim_yates May 04 '12 at 16:00