Groovy方法调用说明

在一个Groovy应用中,会调用三种对象:

  1. POJO 普通java对象
    extends java.lang.Object
  2. POGO 普通Groovy对象
    extends java.lang.Object
    implements groovy.lang.GroovyObject
  3. Groovy拦截器
    extends java.lang.Object
    implements groovy.lang.GroovyInterceptable
    [注意:interface groovy.lang.GroovyInterceptable extend groovy.lang.GroovyObject][GroovyInterceptable中没有抽象方法,它只是起标记作用的接口]

而在Groovy中,动态地注入方法、调用方法、属性就是使用元类metaClass来完成的(类似于Java的反射机制),请求的方法会被委托到这个类。

  1. 对于POJO而言,它是一个普通的java对象,甚至有可能是一个定义在jdk中的类,原生不可能有metaClass。为了实现这一功能,Groovy维护一个名为“MetaClassRegistry”的Map类对象,对于每一个POJO都能找到其对应的metaClass。
    在POJO的metaClass定义的方法和拦截器都优先于POJO中定义的方法
    考虑下面的例子,有一个java类:
    public class Dog {
        public void bark(){
            System.out.println("Wow");
        }
    }
    
    下面在Groovy脚本文件中有如下代码:
    Dog.metaClass.bark={->'don't bark!'}
    println  new Dog().bark()
    
    上述代码输出“don't bark!”,而不是"Wow"
  2. 对于一个拦截器对象,在其上调用的所有方法都会被代理到invokeMethod上,无论存不存在。
    class MyInterceptor implements groovy.lang.GroovyInterceptable{
        def introduceLang(){
            "Groovy"
        }
        def invokeMethod(String name,args){ //注意invokeMethod的签名
            "Hello! Welcome to use MyInterceptor."
        }
    }
    
    println(new MyInterceptor().introduceLang())
    println(new MyInterceptor().unknownMethod())
    
    上述定义了一个拦截器MyInterceptor,然后在拦截器实例上调用了一个存在的方法introduceLang以及不存在的方法unknownMethod,但是它们的输出都是“Hello! Welcome to use MyInterceptor.”。即无论方法存在与否,拦截器都会路由到invokeMethod上。
  3. 如果是POGO对象,它内部就有一个metaClass的属性,指向它的元类。在上面调用方法的路由规则比较复杂:
    Groovy方法调用说明_第1张图片
    POGO上的方法调用

    由于拦截器是特殊的POGO,所以第一步先判断是否是拦截器,如果是就按照第2条路由;否则看看方法在不在元类中,如果在就直接调用(即元类方法优先级高于自身方法),不然就看是否在类中;否则看是否存在同名的闭包属性,如果有则调用闭包;否则看看是否存在一个特殊的方法methodMissing方法,如果有就调用它;否则看看有没有invokeMethod方法(默认实现是抛出异常),如果有则调用,如果没有就抛出异常。
    下面是一些说明,其中的类都是POGO(且不是拦截器):
    • class ClassWithInvokeOnly{
        def invokeMethod(String name,args){'invoke called'}
      }
      def obj=new ClassWithInvokeOnly();
      assert 'invoke called'==obj.unknownMethod(); //断言为真
      
      路由到达最后一步,因为它存在invokeMethod,所以被调用了。
    •   class ClassWithInvokeOnly{
          def methodMissing(String name,args){ //注意方法签名
              'missing called'
          }
        }
        def obj=new ClassWithInvokeOnly();
        assert 'missing called'==obj.unknownMethod();  //断言为真
      
      路由到达倒数第二步,因为它存在methodMissing,所以被调用。
    •   class ClassWithInvokeOnly{
            def closure={
                'closureString'
            }
        }
        def obj=new ClassWithInvokeOnly();
        assert 'closureString'==obj.closure();//断言为真
      
      调用到倒数第三步,因为有同名闭包属性,所以被调用。
    •   class ClassWithInvokeOnly{
            def sayHello(){
                'hello'
            }
        }
        ClassWithInvokeOnly.metaClass.sayHello={
            'metaClass hello'
        }
        def obj=new ClassWithInvokeOnly();
        assert 'metaClass hello'==obj.sayHello(); //断言为真
      
      路由到第二步,元类中的方法会覆盖类中原本的同名方法。
      但是考虑下面的情况:
        class ClassWithInvokeOnly{
            def sayHello(name){
                'hello '+name
            }
        }
        ClassWithInvokeOnly.metaClass.sayHello={
            'metaClass hello'
        }
        def obj=new ClassWithInvokeOnly();
        assert 'hello Bob'==obj.sayHello(“Bob”); //断言为真
      
      上述情况总,因为元类中方法没有有参数的方法,但是类中本身有带参数的同名方法,所以会调用。实际上,在发现元类中不存在这个方法后就会在本类中寻找是否有该方法,如果有就调用,然后找到了,程序于是就终止了。

你可能感兴趣的:(Groovy方法调用说明)