2011-05-06 6 views
7

C'è un modo in Groovy di poter aggiungere codice a un costruttore quando viene creata un'istanza di una classe? Ho una classe Groovy (ma non posso modificare la fonte di questo particolare), ma speravo che ci fosse un modo per iniettare il codice (magari tramite il metaclass) così il mio codice verrà eseguito come parte del costruttore (in questo caso c'è solo uno, costruttore predefinito).Groovy che aggiunge codice a un costruttore

Grazie, Jeff

+2

mai fatto io stesso, ma questo potrebbe aiutare http://groovy.codehaus.org/ExpandoMetaClass+-+Constructors –

+0

collegamento eccellente, grazie –

risposta

8

È possibile ignorare il costruttore, ma è un po 'difficile, soprattutto se si sta sovrascrivendo il costruttore di default. È necessario assegnare una chiusura al numero metaClass.constructor della classe e la chiusura dovrebbe restituire una nuova istanza. La parte difficile è che se chiami il costruttore che hai superato, entrerai in un ciclo ricorsivo e genererai un overflow dello stack. È necessario un altro modo per ottenere un'istanza della classe, ad esempio un costruttore diverso.

Per il test, a volte è possibile aggirare questa limitazione. Solitamente, è sufficiente prima istanziare un oggetto, quindi sovrascrivere il costruttore per restituire l'istanza esistente. Esempio:

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

penso che dovrei essere in grado di utilizzare 2 costruttori per ottenere quello che mi serve compiuto. Grazie. –

2

È possibile aggiungere nuovi costruttori o sostituire quello vecchio. Se è necessario il costruttore originale, è possibile utilizzare la riflessione per questo:

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 
} 

Nota che devi cambiare questo se si dispone dei parametri ai costruttori, per esempio:

// 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: Si noti che quando vuoi aggiungere costruttori che non sono ancora esistenti, usa invece l'operatore <<: MyObject.metaClass.constructor << { /* as above */ }.

1

È possibile ignorare le limitazioni nella soluzione proposta memorizzando il costruttore originale utilizzando la riflessione Java standard. Per esempio, questo è quello che faccio inizializzare una classe (iniezione base) in un test 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 
    } 
} 

Questo viene richiamato solo una volta, ma eveytime qualcuno chiama un costruttore ottengo un'istanza diversa evitare valori di pulizia e garantendo filo sicurezza.