2011-05-06 26 views
7

¿Hay alguna manera en Groovy de que pueda agregar código a un constructor cuando se crea una instancia de una clase? Tengo una clase Groovy (pero no puedo modificar la fuente de este en particular), pero esperaba que hubiera una forma de insertar código (tal vez a través de la metaclase) para que mi código se ejecutara como parte del constructor (en este caso hay solo uno, constructor por defecto).Groovy agregar código a un constructor

Gracias, Jeff

+2

nunca hizo eso a mí mismo, pero esto podría ayudar a http://groovy.codehaus.org/ExpandoMetaClass+-+Constructors –

+0

enlace excelente, gracias –

Respuesta

8

Usted pueden anular el constructor, pero es un poco difícil, especialmente si usted está reemplazando el constructor por defecto. Debe asignar un cierre al metaClass.constructor de la clase, y el cierre debe devolver una nueva instancia. La parte difícil es que si llama al constructor que ha reemplazado, ingresará en un bucle recursivo y generará un desbordamiento de la pila. Necesita otra forma de obtener una instancia de la clase, como un constructor diferente.

Para las pruebas, a veces es posible sortear esta limitación. Por lo general, basta con crear una instancia de un objeto, y luego anular el constructor para devolver la instancia existente. Ejemplo:

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" 
+0

creo que debería ser capaz de utilizar 2 constructores para conseguir lo que necesito consumado. Gracias. –

2

Es posible añadir nuevos constructores o reemplazar el antiguo. Si necesita el constructor original, puede utilizar la reflexión para que:

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 
} 

Tenga en cuenta que usted tiene que cambiar esto si tiene parámetros a los constructores, por ejemplo:

// 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: Tenga en cuenta que cuando Si desea agregar constructores que aún no existen, utilice el operador << en su lugar: MyObject.metaClass.constructor << { /* as above */ }.

1

Puede omitir las limitaciones de la solución propuesta almacenando el constructor original utilizando la reflexión estándar de Java. Por ejemplo, esto es lo que hago inicializar una clase (inyección básica) en una prueba de Spock:

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 
    } 
} 

Esto se invoca sólo una vez, pero eveytime alguien llama a un constructor consigo una instancia diferente evitando los valores de limpieza y asegurar el hilo la seguridad.

Cuestiones relacionadas