12

Ho un POJO che utilizza un servizio di fare qualcosa:Utilizzando Groovy metaclasse sovrascrivere Metodi

public class PlainOldJavaObject { 

    private IService service; 

    public String publicMethod(String x) { 
     return doCallService(x); 
    } 

    public String doCallService(String x) { 
     if(service == null) { 
      throw new RuntimeException("Service must not be null"); 
     } 
     return service.callX(x); 
    } 

    public interface IService { 
     String callX(Object o); 
    } 
} 

E ho un banco di prova Groovy:

class GTest extends GroovyTestCase { 

    def testInjectedMockIFace() { 
     def pojo = new PlainOldJavaObject(service: { callX: "very groovy" } as IService) 
     assert "very groovy" == pojo.publicMethod("arg") 
    } 

    def testMetaClass() { 
     def pojo = new PlainOldJavaObject() 
     pojo.metaClass.doCallService = { String s -> 
      "no service" 
     } 
     assert "no service" == pojo.publicMethod("arg") 
    } 
} 

Il primo metodo di prova, testInjectedMockIFace opere come previsto: il POJO viene creato con un'implementazione dinamica di IService. Quando viene invocato callX, restituisce semplicemente "molto groovy". In questo modo, il servizio viene deriso.

Tuttavia, non capisco perché il secondo metodo, testMetaClass non funzioni come previsto, ma lancia una NullPointerException quando si tenta di richiamare callX sull'oggetto servizio. Pensavo di aver sovrascritto il metodo doCallService con questa linea:

pojo.metaClass.doCallService = { String s -> 

Che cosa sto facendo di sbagliato?

Grazie!

risposta

16

La sintassi è leggermente ridotta. Il problema è che pojo è un oggetto Java e non ha una meta class.Per intercettare le chiamate verso di PlainOldJavaObject doCallService utilizzando ExpandoMetaClass:

basta sostituire:

pojo.metaClass.doCallService = { String s -> 
     "no service" 
    } 

Con:

PlainOldJavaObject.metaClass.doCallService = { String s -> 
     "no service" 
    } 
+3

Una cosa da tenere a mente qui è quando si manipola la metaClass della classe, ogni istanza da quel punto in avanti verrà manipolata. Questo può avere un grande impatto su altri test eseguiti nella stessa sessione. Quando si manipola un'istanza di una classe, viene interessata solo quell'istanza. –

1

Quello che hai sembra buono. Ho eseguito una versione leggermente modificata su questa app web della console e funzionava senza problemi. Guardate voi stessi usando questo codice allo http://groovyconsole.appspot.com/.

public interface IService { 
    String callX(Object o); 
} 

public class PlainOldJavaObject { 

    private IService service; 

    public String publicMethod(String x) { 
     return doCallService(x); 
    } 

    public String doCallService(String x) { 
     if(service == null) { 
      throw new RuntimeException("Service must not be null"); 
     } 
     return service.callX(x); 
    } 
} 

def pojo = new PlainOldJavaObject() 
pojo.metaClass.doCallService = { String s -> 
    "no service" 
} 
println pojo.publicMethod("arg") 

Quale versione di Groovy stai usando. Potrebbe benissimo essere un bug in Groovy nell'implementazione della metaclasse. Il linguaggio groovy si muove abbastanza velocemente e l'implementazione della metaclasse cambia da versione a versione.

Edit - Risposte da Commento:

La versione della console webapp Groovy è 1,7-rc-1. Quindi sembra che quella versione possa funzionare come vuoi tu. Sono attualmente in RC2 quindi mi aspetto che venga rilasciato presto. Non sono sicuro se quello che stai vedendo è un bug o solo una differenza nel modo in cui funziona nella versione 1.6.x.

+0

Ciao Chris, corro Groovy Versione: 1.6.5 JVM: 1.6.0_13 – raoulsson

+0

La versione non ha niente a che fare con ciò Il problema è che doCallService (x) è codice Java, non Groovy, quindi non è metaClass. – noah

15

Se il tuo POJO è davvero una classe Java e non una classe Groovy, questo è il tuo problema. Le classi Java non invocano metodi tramite la metaClass. ad esempio, in Groovy:

pojo.publicMethod('arg') 

è equivalente alla presente Java:

invokeMethod invia la chiamata attraverso il metaclasse. Ma questo metodo:

public String publicMethod(String x) { 
    return doCallService(x); 
} 

è un metodo Java. Non usa invokeMethod per chiamare doCallService. Per far funzionare il tuo codice, PlainOldJavaObject deve essere una classe Groovy in modo che tutte le chiamate passino attraverso la metaClass. Il normale codice Java non utilizza metaClass.

In breve: anche Groovy non può eseguire l'override delle chiamate al metodo Java, può solo eseguire l'override delle chiamate da Groovy o altrimenti inviato tramite invokeMethod.

+0

Come si distingue correttamente tra codice Groovy e codice Java? Come faresti PlainOldGroovyObject invece di PlainOldJavaObject? – Tomato

+4

Se si trova in un file .groovy, è una classe Groovy. – noah

+0

capito. grazie per il chiarimento :) – Tomato

Problemi correlati