Not so long ago, I asked a question in the Groovy mail-list - is there any sustainable list of things that should be avoided when writing a high-performance Groovy. Among other tips, one of the main developers of Groovy, Jochen "blackdrag" Theodorou pointed out that in general, often using a particular type when declaring a variable (for example, MyType var = ... instead of def var = ...) can degrade the production due to overhead of type checking and, if necessary, their cast.
According to the Groovy developers, many of the problems in this area were observed in versions up to 1.7 and were then corrected during a large work on the overall runtime optimization done in version 1.9. Below, however, is a small experiment that shows this overhead even for Groovy 1.8.3.
Before this experiment, it will be useful to view the following article -
groovy.codehaus.org/From+source+code+to+bytecode , which tells how to
step by step the source code on Groovy is converted into JVM bytecode, as well as read some introductory An article in the bytecode itself, for example this one -
www.ibm.com/developerworks/ibm/library/it-haggar_bytecode .
')
So, the simplest code:
class NoStrictType { void myMethod1() { def a = 4.2 } } class StrictType { void myMethod1() { int a = 4.2 } }
The only difference is that in the second class the type of a local variable is explicitly defined. We compile both classes and look at the bytecode of these methods.
For the first case (without a specific type):

So, the code is quite obvious. We omit the first line, this is getting an array of cached CallSites, they are not directly related to type operations. Next, we create a new object of type BigDecimal (everyone remembers that Groovy defaults to all non-integer numbers as BidDecimal?), Duplicate the current value on the top of the operand stack, retrieve the value 4.2 from the pool of constants, initialize the BigDecimal object, save the reference to this created object in the second cell of the array of local variables of the current frame, then load it from there to the top of the stack, and finally use pop; return to return this link from the method. Again, as everyone remembers, in Groovy, even without an explicit return operator, any method returns the last variable that was used in the calculations (more precisely, the last link stored on top of the operand stack at the time the method ended).
Now, bytecode for the second class, StrictType.

What is the difference compared to the first case? A call to the static method DefaultTypeTransformation.intUnbox () has been added. Let's see in the documentation for this method what it does.
groovy.codehaus.org/api/org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.htmlWe see that this method simply converts the reference object into a Number object, and returns a primitive.
public static int intUnbox(Object value) { Number n = castToNumber(value, int.class); return n.intValue(); }
We are looking at how this type conversion is performed:
public static Number castToNumber(Object object, Class type) { if (object instanceof Number) return (Number) object; if (object instanceof Character) { return Integer.valueOf(((Character) object).charValue()); } if (object instanceof GString) { String c = ((GString) object).toString(); if (c.length() == 1) { return Integer.valueOf(c.charAt(0)); } else { throw new GroovyCastException(c, type); } } if (object instanceof String) { String c = (String) object; if (c.length() == 1) { return Integer.valueOf(c.charAt(0)); } else { throw new GroovyCastException(c, type); } } throw new GroovyCastException(object, type); }
Several instanceof statements that are not the cheapest, several explicit type conversions, conditional statements, work with exceptions. I didn’t measure how it affects the speed of work in real applications, but judging by the very fact, how many additional bytecodes need to be performed in this case for type conversion is impressive. Remember - the original method itself - only 10 bytecodes.