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?
Variables in Groovy can be defined in two ways − using the native syntax for the data type or the next is by using the def keyword. For variable definitions it is mandatory to either provide a type name explicitly or to use "def" in replacement. This is required by the Groovy parser.
Groovy data types can be categorized into simple data types and collective data types. Simple data types include strings, regular expressions (regexes), and numbers. Collective data types include lists, maps, and ranges.
You can use the getClass() method to determine the class of an object. Also, if you want to check if an object implements an Interface or Class, you can use the instanceof keyword. That's it about checking the datatype of an object in Groovy.
Groovy - Data Types. In any programming language, you need to use various variables to store various types of information. Variables are nothing but reserved memory locations to store values. This means that when you create a variable you reserve some space in memory to store the value associated with the variable.
We can find the type of any object using instanceof, getClass () and .class operators: Moreover, we can also use the Groovy membership operator in: 5. Conclusion
The literal values in Groovy are similar to those in Java, but Groovy allows for generic variables that can hold any type and provides no enforcement and it allows you to declare variables with types and then enforce the type. Declaring a varable using def allows for the flexibility most dynamic programming languges provide.
instanceof is a binary operator that we can use to check if an object is an instance of a given type. It returns true if the object is an instance of that particular type and false otherwise. Also, Groovy 3 adds the new !instanceof operator. It returns true if the object is not an instance of a type and false otherwise.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With