Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Groovy adding code to a constructor

Is there a way in Groovy that I can add code to a constructor when a class is instantiated? I have a Groovy class (but I can't modify the source of this particular one), but I was hoping there was a way to inject code (maybe via the metaclass) so my code gets run as part of the constructor (in this case there is only one, default constructor).

Thanks, Jeff

like image 339
Jeff Storey Avatar asked May 06 '11 06:05

Jeff Storey


People also ask

What is MetaClass in Groovy?

A MetaClass within Groovy defines the behaviour of any given Groovy or Java class. The MetaClass interface defines two parts. The client API, which is defined via the extend MetaObjectProtocol interface and the contract with the Groovy runtime system.

How do I instantiate a class in Groovy?

The Class method in Groovy has a newInstance() to dynamically create a new instance of a given class. We can use an Object array or Map as argument if we want to invoke the non-default constructor of the class.

How do I create a new object in Groovy?

To create an object by using positional parameters, the respective class needs to declare one or more constructors. In the case of multiple constructors, each must have a unique type signature. The constructors can also added to the class using the groovy. transform.


2 Answers

You can override the constructor, but it's a little tricky, particularly if you're overriding the default constructor. You need to assign a closure to the class's metaClass.constructor, and the closure should return a new instance. The tricky part is that if you call the constructor you've overriden, you'll get into a recursive loop and generate a stack overflow. You need another way to get an instance of the class, such as a different constructor.

For testing, it's sometimes possible to get around this limitation. Usually, it's enough to first instantiate an object, then override the constructor to return the existing instance. Example:

class MyObject {
    String something
    MyObject() { something = "initialized" }
}

testInstance = new MyObject()
testInstance.something = "overriden"
MyObject.metaClass.constructor = { -> testInstance }

aNewObject = new MyObject()
assert aNewObject.is(testInstance)
assert aNewObject.something == "overriden"
like image 84
ataylor Avatar answered Sep 28 '22 03:09

ataylor


It is possible to add new constructors or replace the old one. If you need the original constructor, you can use reflection for that:

MyObject.metaClass.constructor = { -> // for the no-arg ctor
  // use reflection to get the original constructor
  def constructor = MyObject.class.getConstructor()
  // create the new instance
  def instance = constructor.newInstance()
  // ... do some further stuff with the instance ...
  println "Created ${instance}"
  instance
}

Note that you have to change this if you have parameters to your constructors, e.g:

// Note that the closure contains the signature of the constructor
MyObject.metaClass.constructor = { int year, String reason ->
  def constructor = MyObject.class.getConstructor(Integer.TYPE, String.class)
  def instance = constructor.newInstance(
    2014, "Boy, am I really answering a question three years old?")
  // ... do some further stuff with the instance ...
  println "Created ${instance}"
  instance
}

PS: Note that when you want to add constructors which are not yet existent, use the << operator instead: MyObject.metaClass.constructor << { /* as above */ }.

like image 28
ricma Avatar answered Sep 28 '22 04:09

ricma