在Groovy中声明类型

knp*_*wrs 7 groovy types

当你没有在groovy中为变量声明一个类型时,我的理解是java虚拟机必须使用反射才能在执行任何方法之前弄清楚对象的类型并且有可能引发运行时错误.

如果这是正确的,那么当您声明类型时呢?java虚拟机是否仍然使用反射,因为原始代码是groovy?或者首先我对这整件事的理解是否正确?

Ted*_*eid 22

调查此类问题的最佳方法是自己查看生成的字节码.如果您创建两个示例类:

WithType.groovy:

class WithType {
    String name

    String returnName() { getName() }
}
Run Code Online (Sandbox Code Playgroud)

WithoutType.groovy:

class WithoutType {
    def name

    def returnName() { getName() }
}
Run Code Online (Sandbox Code Playgroud)

编译它们groovyc:

% groovyc WithType.groovy
% groovyc WithoutType.groovy
Run Code Online (Sandbox Code Playgroud)

然后javap用来吐出人类可读的字节码:

% javap -c WithType > WithType.txt
% javap -c WithoutType > WithoutType.txt
Run Code Online (Sandbox Code Playgroud)

然后,您可以对两个文件进行returnName()区分,并查找方法,以查看它们如何getName()在名称字段中调用生成的方法时的处理方式不同.如果找到该returnName()方法,您会看到WithType版本如下所示:

public java.lang.String returnName();
  Code:
   0:   invokestatic    #24; //Method $getCallSiteArray:()[Lorg/codehaus/groovy/runtime/callsite/CallSite;
   3:   astore_1
   4:   aload_1
   5:   ldc #47; //int 0
   7:   aaload
   8:   aload_0
   9:   invokeinterface #53,  2; //InterfaceMethod org/codehaus/groovy/runtime/callsite/CallSite.callCurrent:(Lgroovy/lang/GroovyObject;)Ljava/lang/Object;
   14:  invokestatic    #56; //Method $get$$class$java$lang$String:()Ljava/lang/Class;
   17:  invokestatic    #38; //Method org/codehaus/groovy/runtime/ScriptBytecodeAdapter.castToType:(Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;
   20:  checkcast   #58; //class java/lang/String
   23:  areturn
   24:  nop
Run Code Online (Sandbox Code Playgroud)

而无类型的人看起来像这样:

public java.lang.Object returnName();
  Code:
   0:   invokestatic    #24; //Method $getCallSiteArray:()[Lorg/codehaus/groovy/runtime/callsite/CallSite;
   3:   astore_1
   4:   aload_1
   5:   ldc #47; //int 0
   7:   aaload
   8:   aload_0
   9:   invokeinterface #53,  2; //InterfaceMethod org/codehaus/groovy/runtime/callsite/CallSite.callCurrent:(Lgroovy/lang/GroovyObject;)Ljava/lang/Object;
   14:  areturn
   15:  nop
Run Code Online (Sandbox Code Playgroud)

无类型的指令具有较少的指令,因为它不需要对它返回的内容进行任何类型检查或字符串转换.实际的方法调用getName()在类型和非类型化版本中都是相同的:

   9:   invokeinterface #53,  2; //InterfaceMethod org/codehaus/groovy/runtime/callsite/CallSite.callCurrent:(Lgroovy/lang/GroovyObject;)Ljava/lang/Object;
Run Code Online (Sandbox Code Playgroud)

你可以看到它在groovy CallSite方法上调用一个接口方法并传入一个GroovyObject. CallSite是一个由一堆groovy元对象代码实现的接口.所以这是对动态调用getName()方法的groovy MOP的调用.

(这都是groovy 1.7.5)


ig0*_*774 7

看起来好像类型声明对Groovy如何调用方法有任何特定的影响.本质上,正如Groovy wiki所指出的,Groovy不是简单地调用该方法,而是调用invokeMethod()对象的元类,该元类委托给元类中定义的方法或执行方法的反射查找.

值得注意的是,元类使用MetaMethod,在最坏的情况下,它使用缓存的反射查找,即,它只需要进行单个反射查找.

编辑

使用groovypp可以避免一些开销,这为Groovy代码添加了静态类型.