在Groovy中声明类型

时间:2011-03-17 11:29:02

标签: groovy types

当你没有在groovy中为变量声明一个类型时,我的理解是java虚拟机必须使用反射来确定对象在执行任何方法之前的类型并且有可能抛出运行时错误。

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

5 个答案:

答案 0 :(得分:22)

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

WithType.groovy:

class WithType {
    String name

    String returnName() { getName() }
}

WithoutType.groovy:

class WithoutType {
    def name

    def returnName() { getName() }
}

使用groovyc编译它们:

% groovyc WithType.groovy
% groovyc WithoutType.groovy

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

% javap -c WithType > WithType.txt
% javap -c WithoutType > WithoutType.txt

然后,您可以对2个文件进行区分,并查找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

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

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

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

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

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

(这都是groovy 1.7.5)

答案 1 :(得分:7)

看起来好像类型声明对Groovy如何调用方法有任何特定的影响。基本上,正如所提到的on the Groovy wiki,Groovy不是简单地调用该方法,而是在对象的元类上调用invokeMethod(),该元类委托给元类中定义的方法或者执行方法的反射查找。 / p>

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

修改

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

答案 2 :(得分:2)

它仍然大量使用反射 您必须考虑的一件事是,方法调用在运行时被解析 最容易看到的方式

Integer.metaClass.xxx << {println "hi"}
Integer ten = 10
ten.xxx()

这会编译,即使普通的Integer 100没有方法xxx 第一行将该方法添加到类中,但编译器在编译时不会知道这个(在运行时添加该方法)。
类型已知并不重要,groovy仍然使用反射来执行调用 另一个例子。

def (Number i, Number l) = [100, 100L]
print i
print l
def print(Number n)  {println "Number $n"}
def print(Integer n) {println "Integer $n"}

在java中,它将打印数字100两次,因为该方法是静态选择的 Groovy并不关心,只是在运行时根据参数类选择方法。

答案 3 :(得分:0)

有一些图表可以从groovy ++的创建者那里回答你的问题:

https://code.google.com/p/groovypptest/wiki/Performance

他们显示Groovy很慢(取决于算法慢10到100倍)。这不仅是由动态类型,过度使用反射和动态调用的方法引起的。它将对象数组作为列表处理。所以它完全围绕java的本机数组处理。这使得它也比Java慢。甚至groovy ++(优化groovy程序的最简单方法)确实解决了这个问题。

但是无论groovy代码运行得多么缓慢,我都非常喜欢这种语言的能力以及你可以用groovy中的几行代替大量的jave行。每种语言都有其目的。 Java有利于灵活性和可伸缩性,ruby用于快速原型设计(不是用于扩展),Groovy是一个非常好的解决方案,可以编写快速原型,具有java的所有功能,几乎与java代码一样可扩展(也可以替换)使用java或g ++一步一步地慢速运行groovy代码,因为你需要它。)

答案 4 :(得分:-3)

嗨,这是一个很好的问题,但不知道JVM是否使用反射来找到这些类型。 编写groovy时必须要完成的事情是Java和Groovy只在源代码中有所不同。当它们运行时,它们已被绑定到java对象模型。无论您是编写Groovy类还是脚本,它们都在JVM中作为Java类运行。所以无论你做什么,所有这些都转换为Java并在JVM中运行:)

虽然Java运行时理解编译的Groovy类没有任何 问题,它不了解.groovy源文件。还有更多的工作要做 如果你想在运行时动态加载.groovy文件,请在幕后。 确保Groovy语法面向行,但执行不是。那就是Groovy coed没有被处理逐行(震惊!,你必须,一些技巧正在进行)而是Groovy被完全解析,并且已经生成了一个类。这个生成的类充当Java和Groovy.Groovy类之间的桥梁,生成它们的格式与Java字节码相同。正如我之前提到的,由于Groovy生成相同的字节码,并且它在Jvm中运行,所以类加载器可以处理这个字节码。如果它听起来很刺耳,请不要担心,所有的事情都是由 Groovy 为我们完成的。

我建议您阅读 Groovy in action 一书。点击here