开发者

Declaring Types in Groovy

开发者 https://www.devze.com 2023-02-18 13:07 出处:网络
When you don\'t declare a type for a variable in groovy, it is my understanding that the java virtual machine has to use reflection in order to figure out what type the object is before executing any

When you don't declare a type for a variable in groovy, it is my understanding that the java virtual machine has to use reflection in order to figure out what type the object is before executing any methods and has the potential of throwing a runtime error.

If that is correct, what about when you declare the type? Does the java virtual machine still end up using reflection because the original code was in groovy? Or is my开发者_开发技巧 understanding of this whole matter incorrect in the first place?


The best way to investigate this kind of problem is to look at the generated byte-code yourself. If you create two sample classes:

WithType.groovy:

class WithType {
    String name

    String returnName() { getName() }
}

WithoutType.groovy:

class WithoutType {
    def name

    def returnName() { getName() }
}

Compile them with groovyc:

% groovyc WithType.groovy
% groovyc WithoutType.groovy

And then use javap to spit out the human readable bytecode:

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

You can then diff the 2 files and look for the returnName() method to see how they're handled differently in how they call the generated getName() method for the name field. If you find the returnName() method, you'll see that the WithType version looks like this:

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

and the untyped one looks like this:

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

The untyped one has fewer instructions because it doesn't need to do any type checking or String conversion on what it's returning. The actual method call to getName() is the same in both the typed and untyped versions:

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

You can see that it's calling an interface method on the groovy CallSite method and passing in a GroovyObject. CallSite is an interface that's implemented by a bunch of the groovy metaObject code. So this is the call to the groovy MOP that dynamically invokes the getName() method.

(this is all with groovy 1.7.5)


It doesn't appear as though type declarations have any specific influence on how Groovy invokes the method. Essentially, as noted on the Groovy wiki, instead of simply invoking the method, Groovy will call invokeMethod() on the object's metaclass, which either delegates to a method defined in the metaclass or performs a reflective lookup of the method.

It's worth noting that the metaclass makes use of a MetaMethod, which, in the worst case, uses a cached reflection lookup, i.e., it only needs to make a single reflective lookup.

Edit

Some of this overhead may be avoidable by using groovypp, which adds static typing capabilities to Groovy code.


It still makes heavy use of reflection.
One of the things you must have in mind, is that the methods calls are resolved at runtime.
Easiest way to see it

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

This compiles, even though a normal Integer 100 would not have method xxx.
The first line adds the method to the class, but the compiler would not know this at compile time (the method is added at runtime).
It does not matter that the type is known, groovy still uses reflection to perform the calls.
Another example.

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"}

In java, it would print Number 100 twice, as the method is statically selected.
Groovy doesn't care and just selects the method based on the class of the arguments at runtime.


There are some charts to answer your question from the creators of groovy++ on:

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

They are showing Groovy is slow (depending on the algorithm between 10 and 100 times slower). This is not only caused by dynamic typing, excessive use of reflection and dynamically invoked methods. It handles object arrays as lists. So it works completely around the native array handling of java. Which makes it also slower than Java. And not even groovy++ (the easiest way to optimize groovy programs) does address this fact.

But no matter how slow groovy code runs, I really like the abilities of the language and the fact that you can replace tons of jave lines with a few lines in groovy. Each language has its purpose. Java is good for flexibility and scalability, ruby is for fast prototyping (not so much for scaling) and Groovy is a very good solution to write fast prototypes with all the functionality of java that are almost as scalable as java code (also you can replace the slow running groovy code step by step with java or g++, as you need it).


Hi it was a nice question but don't know whether that the JVM uses reflection to found these types. The thing you have to be through when you code groovy is that both Java and Groovy have differs only in the source code. When they have been run they have been bound to java object model. Regardless of whether you write Groovy classes or scripts, they run as Java classes inside the JVM. So regardless what you do, all these are converted to Java and run in JVM :)

Although the Java runtime understands compiled Groovy classes without any problem, it doesn’t understand .groovy source files. More work has to happen behind the scenes if you want to load .groovy files dynamically at runtime. Make sure that Groovy syntax is line-oriented, but the execution is not. That is Groovy coed is not processed Line By Line(shocked!,ya have to, some trick is going on) Instead Groovy is fully parsed, and a class is been generated. This generated class act as a bridge between Java And Groovy.Groovy classes are generated such that their format is identical to Java bytecode. So as i mentioned before, since Groovy produce the same bytecode, and it runs in Jvm, the class loader can handle and this bytecode. If it sounds harsh, don't worry, all the things are done by Groovy by it, for us.

I recommend you to read Groovy in action book. Click here

0

精彩评论

暂无评论...
验证码 换一张
取 消