在Groovy Console中,++(++(++(++1++)++)++)++评估为5!
为什么?
我期望一个错误,文字常量不能递增!
Pre-Increment给出下一个Integer,而Post-Increment给出相同的Integer,但我希望两者在与文字常量一起使用时抛出错误.
[[我在带有Java 1.8.0_101的Groovy 2.4.10的Windows 10上运行]]
好问题,我很想看到一个权威的答案.
我在这上面打开了一个标签但仍然没有看到,所以我调查了自己.如果你有耐心阅读,下面是一个简单的Groovy用户(你真正的)的观点,用我拥有的工具和不完整的知识(希望不要太误)来看这个.
简而言之:语言设计和编译器实现更关注转换提供的功能和灵活性,这显然是以放松严格性为代价的.
Groovy在设计上非常宽松,有时候可能有点太多了.
if (condition) int foo=1javac因为"这里不允许声明"而被拒绝,因为在那里声明一个变量是没有意义的,必须有一个错误,更好地检查代码,对吧?Naaah对Groovy来说没问题.
private字段和方法不是私有的.
final是不一致的应用.你可以增加一个final int.这将在2.5中修复,见下文.
finalGroovy 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
Run Code Online (Sandbox Code Playgroud)
但不,Groovy 2.5仍然无法阻止增加常量:
$ ./groovy-2.4.11/bin/groovy -e "println(++1)"
2
$ ./groovy-2.5.0-alpha-1/bin/groovy -e "println(++1)"
2
Run Code Online (Sandbox Code Playgroud)
那么那里发生了++1什么?
我认为第一个重要的是Groovy不是Java,它不会转换为Java语言.它编译为字节码.这里++有很多不同的Groovy和Java.
Groovy有9个编译阶段(参见org.codehaus.groovy.control.CompilePhase):初始化,解析,转换,语义分析,规范化,指令选择,类生成,输出,终结.
我们可以使用GroovyConsole探索它们.让我们在这个简单的片段上开始吧:
def foo() {
++1
}
Run Code Online (Sandbox Code Playgroud)
我在编译和解析阶段没有看到任何内容,但在转换阶段,我们得到了一些有趣的东西:
public java.lang.Object foo() {
++(1)
}
Run Code Online (Sandbox Code Playgroud)
常数被括号括起来.嗯.有趣,请注意我们可以做到++(2+2)并得到5.这里没有要增加的变量......它似乎++只是一个方法的快捷方式incrementThis(thing),它可以增加事物并返回它,或者如果事物是常量,它会向事物添加1并返回结果.
在我的IDE中如果我按住Ctrl键点击++它会让我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);
}
Run Code Online (Sandbox Code Playgroud)
如果我追踪它,它只会结束self + ONE,所以我不知道改变self发生在哪里,我认为这不是那么简单.我的意思++(x)不仅仅是一条捷径DefaultGroovyMethods.next(x),还有更多.
所以让我们看看如果我分别使用javac和编译以下内容会发生什么groovyc:
//Wat.java:
public class Wat {
public int foo() {
int i=1;
return ++i;
}
}
//wat.groovy:
def foo() {
++1
}
Run Code Online (Sandbox Code Playgroud)
这是我得到的(Java 1.8,Groovy 2.4,专注于相关部分):
// 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
Run Code Online (Sandbox Code Playgroud)
所以对于Groovy ++1来说相当于Integer.valueOf(1+1)(并且严格只需要后座).
但那怎么样def foo(i) { ++i }?我编译了,i显然是一个Object,但我不明白得到的字节码,我仍然看不到DefaultGroovyMethods.next或没有引用NumberNumberPlus.plus.这就是我认为这种分析不足的地方,并希望看到一个权威的答案.