In Groovy Console , ++(++(++(++1++)++)++)++
evaluates to 5
!
Why ?
I would expect an error that literal constants can not be incremented !
Pre-Increment gives the next Integer, while Post-Increment gives the same Integer, but I would expect both to throw errors when used with literal constants.
[[ I am running this on Windows 10 with Groovy 2.4.10 with Java 1.8.0_101 ]]
The increment operator changes a variable by the value of one. Instead of writing varOne = varOne + 1; you can write varOne++; and it will do the same thing.
[:] creates an empty Map. The colon is there to distinguish it from [] , which creates an empty List. This groovy code: def foo = [:]
In groovy, the ==~ operator (aka the "match" operator) is used for regular expression matching. !=
String interpolation. Any Groovy expression can be interpolated in all string literals, apart from single and triple-single-quoted strings. Interpolation is the act of replacing a placeholder in the string with its value upon evaluation of the string. The placeholder expressions are surrounded by ${} .
Nice question, I'd love to see an authoritative answer.
I kept a tab open on this and still see nothing, so I investigated myself. If you have the patience to read that, below is the perspective of a simple Groovy user (yours truly), looking at this with the tools I have and incomplete knowledge (hopefully not too mistaken).
In short: the language design & compiler implementation care more about the power & flexibility offered by transformations, and this apparently comes at the cost of relaxing the strictness.
Groovy is very permissive by design, and sometimes that might be a bit too much.
if (condition) int foo=1
is rejected by javac
with "Declaration is not allowed here", because it makes no sense to declare a variable there, there must be a mistake, better check the code, right? Naaah it's OK for Groovy.
private
fields and methods are not private.
final
is applied inconsistently. You can increment a final int
. That will be fixed in 2.5 though, see below.
Difference treatment of final
between Groovy 2.4 & 2.5:
$ ./groovy-2.4.11/bin/groovy -e "final j=0; println(++j)"
1
$ ./groovy-2.5.0-alpha-1/bin/groovy -e "final j=0; println(++j)"
org.codehaus.groovy.control.MultipleCompilationErrorsException: startup failed:
script_from_command_line: 1: The variable [j] is declared final but is reassigned
. At [1:20] @ line 1, column 20.
final j=0; println(++j)
^
1 error
But no, Groovy 2.5 still does not prevent from incrementing a constant:
$ ./groovy-2.4.11/bin/groovy -e "println(++1)"
2
$ ./groovy-2.5.0-alpha-1/bin/groovy -e "println(++1)"
2
So what's happening there with ++1
?
I think the first important thing is Groovy is not Java, and it does not transform to Java language. It compiles to bytecode. And here ++
is vastly different in Groovy & Java.
Groovy has 9 compilation phases (see org.codehaus.groovy.control.CompilePhase): initialization, parsing, conversion, semantic analysis, canonicalization, instruction selection, class generation, output, finalization.
We can explore them with GroovyConsole. Let's start it on this simple snippet:
def foo() {
++1
}
I see nothing in Compilation and Parsing phases, but at Conversion phase, we get something interesting:
public java.lang.Object foo() {
++(1)
}
The constant is surrounded by parentheses. Hmm. Interesting, notice we can do ++(2+2)
and get 5
. There is no variable to increment here... It seems ++
is just a shortcut for a method like incrementThis(thing)
, which either increments the thing and returns it, or if the thing is a constant, it adds 1 to the thing and returns the result.
In my IDE if I ctrl-click on ++
it gets me to org.codehaus.groovy.runtime.DefaultGroovyMethods.next(Number self)
:
/**
* Increment a Number by one.
*
* @param self a Number
* @return an incremented Number
* @since 1.0
*/
public static Number next(Number self) {
return NumberNumberPlus.plus(self, ONE);
}
If I track that down, it only ends up doing self + ONE
, so I don't know where the alteration of self
happens, and I think it's not so simple. I mean ++(x)
is not just a shortcut to DefaultGroovyMethods.next(x)
, there is more to it.
So let's see what happens if I compile the following with javac
and groovyc
respectively:
//Wat.java:
public class Wat {
public int foo() {
int i=1;
return ++i;
}
}
//wat.groovy:
def foo() {
++1
}
Here is what I get (Java 1.8, Groovy 2.4, focusing on the relevant part):
// Java:
0: iconst_1 // load int value 1 onto stack
1: istore_1 // store int value into variable #1
2: iinc 1, 1 // increment local variable #1 by 1
5: iload_1 // load int value from local variable #1
6: ireturn // return that integer value
// Groovy:
43: iconst_1 // load int value 1 onto stack
44: iconst_1 // load another int value 1 onto stack
45: iadd // add those 2 ints (result stored on stack in place of first int, stack is popped by 1)
46: invokestatic #58 // Invoke method java/lang/Integer.valueOf(previous result)
49: areturn // Return that new Integer result
So for Groovy ++1
is equivalent to Integer.valueOf(1+1)
(and strictness simply takes a backseat).
But what about def foo(i) { ++i }
? I compiled that, i
is obviously an Object
, but I don't understand the resulting bytecode, I still see no reference to DefaultGroovyMethods.next
or NumberNumberPlus.plus
. That's where I think this analysis falls short, and would love to see an authoritative answer.
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