为什么Groovy常量可以递增?

时间:2017-05-18 15:55:39

标签: debugging groovy

在Groovy控制台中,++(++(++(++1++)++)++)++评估为5
为什么
我希望错误文字常量不能递增

Pre-Increment给出下一个Integer,而Post-Increment给出相同的Integer,但我希望两者在用于文字常量时都会抛出错误。

[[我在带有Java 1.8.0_101的Groovy 2.4.10的Windows 10上运行]]

1 个答案:

答案 0 :(得分:7)

好问题,我很乐意看到权威的答案。

我打开了一个标签,但仍然没有看到任何内容,所以我调查了自己。如果您有耐心阅读,下面是一个简单的Groovy用户(您真正的)的视角,使用我拥有的工具和不完整的知识(希望不会太误解)来看这个。

简而言之:语言设计&编译器实现更关心功率和功能转型所带来的灵活性,显然是以放宽严格性为代价的。

Groovy在设计上非常宽松,有时候可能有点太多了。

  • if (condition) int foo=1javac拒绝"此处不允许声明",因为在那里声明变量没有任何意义,必定有错误,更好地检查代码,对吧? Naaah对Groovy来说没问题。

  • private字段和方法are not private

  • finalapplied inconsistently。您可以增加final int。这将在2.5中修复,但见下文。

Groovy 2.4&之间final的差异处理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

但不,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

那么++1发生了什么?

我认为第一个重要的事情是Groovy不是Java,它不会转换为Java语言。它编译为字节码。在这里,++在Groovy& Java的。

Groovy有9 compilation phases(参见org.codehaus.groovy.control.CompilePhase):初始化,解析,转换,语义分析,规范化,指令选择,类生成,输出,最终化。

我们可以使用GroovyConsole探索它们。让我们从这个简单的片段开始吧:

def foo() {
    ++1
}

我在编译和解析阶段没有看到任何内容,但在转换阶段,我们得到了一些有趣的东西:

public java.lang.Object foo() {
    ++(1)
}

常数被括号括起来。嗯。有意思,请注意我们可以++(2+2)获取5。这里没有要增加的变量......似乎++只是像incrementThis(thing)这样的方法的快捷方式,它可以递增并返回它,或者如果该东西是常量,它会添加1到事物并返回结果。

在我的IDE中,如果我按住++,它会转到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);
}

如果我追踪到它,它最终只会self + ONE,所以我不知道self发生了什么变化,我认为这不是那么简单。我的意思是++(x)不仅仅是DefaultGroovyMethods.next(x)的快捷方式,还有更多内容。

因此,如果我分别使用javacgroovyc编译以下内容,请看看会发生什么:

//Wat.java:
public class Wat {
    public int foo() {
        int i=1;
        return ++i;
    }
}

//wat.groovy:
def foo() {
  ++1
}

这是我得到的(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

因此对于Groovy ++1相当于Integer.valueOf(1+1) (并且严格性仅仅是后座)。

但是def foo(i) { ++i }呢?我编译说,i显然是Object,但我不理解生成的字节码,我仍然看不到对DefaultGroovyMethods.nextNumberNumberPlus.plus的引用。这就是我认为这种分析不足的地方,并希望看到权威的答案。