Groovy добавление кода в конструктор - PullRequest
7 голосов
/ 06 мая 2011

Есть ли способ в Groovy, чтобы я мог добавить код в конструктор, когда создается экземпляр класса? У меня есть класс Groovy (но я не могу изменить источник этого конкретного), но я надеялся, что был способ внедрить код (возможно, через метакласс), поэтому мой код запускается как часть конструктора (в этом case только один, конструктор по умолчанию).

Спасибо, Джефф

Ответы [ 3 ]

8 голосов
/ 06 мая 2011

Вы можете переопределить конструктор, но это немного сложно, особенно если вы переопределяете конструктор по умолчанию.Вам необходимо назначить замыкание для класса 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"
2 голосов
/ 08 июля 2014

Можно добавить новые конструкторы или заменить старые.Если вам нужен оригинальный конструктор, вы можете использовать отражение для этого:

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 */ }.

1 голос
/ 19 июня 2016

Вы можете обойти ограничения в предлагаемом решении, сохранив оригинальный конструктор, используя стандартное отражение Java. Например, я инициализирую класс (базовое внедрение) в тесте на спок:

def setupSpec() {
    MockPlexusContainer mockPlexusContainer = new MockPlexusContainer()
    def oldConstructor = MY_CLASS.constructors[0]

    MY_CLASS.metaClass.constructor = { ->
        def mojo = oldConstructor.newInstance()
        mockPlexusContainer.initializeContext(mojo)
        return mojo
    }
}

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

...