Groovy将代码添加到构造函数中

时间:2011-05-06 06:07:03

标签: groovy metaprogramming metaclass

在Groovy中是否有一种方法可以在实例化类时向构造函数添加代码?我有一个Groovy类(但我无法修改这个特定的源代码),但我希望有一种方法可以注入代码(可能通过元类),所以我的代码作为构造函数的一部分运行(在此case只有一个默认构造函数。)

谢谢, 杰夫

3 个答案:

答案 0 :(得分:8)

可以覆盖构造函数,但这有点棘手,特别是如果你要覆盖默认的构造函数。您需要为类的metaClass.constructor分配一个闭包,闭包应返回一个新实例。棘手的部分是,如果你调用你重写的构造函数,你将进入一个递归循环并产生一个堆栈溢出。您需要另一种方法来获取类的实例,例如不同的构造函数。

为了进行测试,有时可以绕过这个限制。通常,首先实例化一个对象就足够了,然后覆盖构造函数以返回现有实例。例如:

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"

答案 1 :(得分:2)

可以添加新构造函数或替换旧构造函数。如果您需要原始构造函数,可以使用反射:

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 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:请注意,当您想要添加尚不存在的构造函数时,请改用<<运算符:MyObject.metaClass.constructor << { /* as above */ }

答案 2 :(得分:1)

您可以通过使用标准Java反射存储原始构造函数来绕过所提出的解决方案中的限制。例如,这是我在spock测试中初始化类(基本注入)的原因:

mapreduce.cluster.temp.dir

这只被调用一次,但eveytime有人调用构造函数我得到一个不同的实例,避免清理值并确保线程安全。