Search code examples
javagroovymop

Groovy customize MetaClassImpl cause 'Could not find matching constructor'


Here is a hello world level groovy mop program, just want to customize MetaClassImpl using my own, but it seems groovy not allowed, here is my code:

MyMetaClassImpl.java

import groovy.lang.MetaClassImpl;

public class MyMetaClassImpl extends MetaClassImpl {
    public MyMetaClassImpl(Class theClass) {
        super(theClass);
    }

    @Override
    public Object invokeMethod(Object object, String methodName, Object[] originalArguments) {
        System.out.println("invoke here");
        return super.invokeMethod(object, methodName, originalArguments);
    }
}

Person.groovy

class Person {
    String name
    int age

    String getHabit(String name) {
        String habit = null
        switch (name) {
            case "ZhangSan":
                habit = "soccer"
                break
            case "Lisi":
                habit = "basketball"
                break
            default:
                break
        }
        return habit
    }

    @Override
    Object invokeMethod(String name, Object args) {
        if (name == 'say') {
            println "say method has been invoked"
            return null;
        }
        super.invokeMethod(name, args)
    }
}

PersonGroovyMain.groovy

import cn.crabime.java.meta.MyDelegateMetaClass
import cn.crabime.java.meta.MyMetaClassImpl
import cn.crabime.java.pogo.Person
import org.codehaus.groovy.runtime.InvokerHelper

class PersonGroovyMain {

    static void main(String[] args) {
        invokeNormal()
        
        // cause error line
        def p = new Person()
        println p

    }

    static MetaClassImpl invokeNormal() {
        MyMetaClassImpl norMetaClass = new MyMetaClassImpl(Person.class)
        norMetaClass.initialize()
        InvokerHelper.metaRegistry.setMetaClass(Person.class, norMetaClass)
        return norMetaClass
    }
}

Output error below:

Exception in thread "main" groovy.lang.GroovyRuntimeException: Could not find matching constructor for: cn.crabime.java.pogo.Person()
    at groovy.lang.MetaClassImpl.invokeConstructor(MetaClassImpl.java:1724)
    at groovy.lang.MetaClassImpl.invokeConstructor(MetaClassImpl.java:1527)
    at org.codehaus.groovy.runtime.callsite.MetaClassConstructorSite.callConstructor(MetaClassConstructorSite.java:46)
    at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallConstructor(CallSiteArray.java:57)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:230)
    at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:234)
    at cn.crabime.java.main.PersonGroovyMain.main(PersonGroovyMain.groovy:17)

this question already puzzled me two days, and i don't want to use DelegatingMetaClass, hope somebody can help me out.


Solution

  • use constructor with 3 parameters

    @groovy.transform.ToString
    class A{ String name }
    
    def meta = new MetaClassImpl(GroovySystem.getMetaClassRegistry(), A.class, null){
        @Override
        public Object invokeMethod(Object object, String methodName, Object arguments) {
            if(methodName=='f1' && !arguments){
                return object.name.toUpperCase()
            }
            return super.invokeMethod(object, methodName, arguments)
        }
    }
    meta.initialize()
    GroovySystem.getMetaClassRegistry().setMetaClass(A.class, meta)
    
    def a = new A(name:'world')
    println a.f1()