据说Java会忽略多余的空白.为什么c=a++++b在没有空格的情况下不能编译



在所有关于Java的书中,我都读到编译器以相同的方式处理所有空白,只是忽略额外的空白,因此最好大量使用它们来提高代码的可读性。我在我写的每一句话中都找到了证据:有没有空格,有多少空格都无关紧要(或者我只是没有注意)。

最近,我决定对运算符优先级和关联性进行一些实验,以测试优先级表的实际操作,并尝试编译

int a = 2;
int b = 3;    
int c = a+++b;
int d = a+++++b;

虽然前者的语句编译得很完美,但后者产生了一个异常:

线程"main"java.lang.RuntimeException中的异常:不可执行源代码-意外类型。必需:变量。找到:值。

但是,当我添加空格int d = a++ + ++b时,它进行了编译。为什么会出现这种情况?据说Java无论如何都会忽略多余的空白。(如果这很重要的话,我有Java 8和Netbeans IDE 8.2。)

我想这可能与表达式的解析方式有关,但我不确定。我试着在SO和谷歌上查找了几个关于解析、空格和运算符的问题,但找不到确切的答案。

UPD。为了解决这样的评论,即重要的是"额外",而不是所有的空白:由于int c = a++ + b;int c=a+++b;都是编译的,可以类比地说,在int d = a ++ + ++b;中,空白也是"额外"的。

Java语言规范第3.2节,"词汇翻译",says(emphasis mine):

使用以下三个词汇翻译步骤,将原始Unicode字符流翻译成标记序列,依次应用:

  1. Unicode转义的翻译[…]

  2. 将[…]翻译成输入字符流和行终止符[…]。

  3. 将第2步产生的输入字符流和行终止符转换为输入元素序列(§3.5),该序列在空白(§3.6)和注释(§3.7)被丢弃后,包括标记,即句法语法的终止符号(§2.3)

在每一步都使用尽可能长的翻译,即使结果最终没有形成正确的程序,而另一个词汇翻译会。

因此,在确定"输入元素序列"之后,将丢弃空白字符。第3.5节"输入元素和令牌"说:

空白(§3.6)和注释(§3.7)可以用来分隔标记,如果这些标记相邻,则可能以另一种方式标记化例如,只有在没有空格或注释的情况下,输入中的ASCII字符-和=才能形成运算符标记-=(§3.12)

语法分析器需要了解您在写什么。

从语法分析器的角度来看,如果不在加号和加号之间放置分隔符,就无法理解加号的序列。

在最小值上添加更多的空格不会改变结果。

所以两条线都有相同的结果:

int d = a++ + ++b;
int d = a++     +     ++b;

考虑以下代码:

int d = a +++ b;

你的意图是什么?

int d = a + ++b;

int d = a++ + b;

此外,从人类的角度来看,如果没有额外的空白,就不可能理解。

此外,如果这段代码适用于编译器,从人类的角度来看是不可理解的。

从编译器的角度来看,没有空格的序列a++++++b是不可理解的,因为他试图读取尽可能多的字符以确定导致序列a ++ ++ + b不是有效的令牌序列的令牌。


无论如何,我的建议是从人类的角度尽可能保持代码的干净,这样它将更容易维护、阅读和增强。因此,在需要时使用空格,不要滥用空格,但如果生成的代码可读性较差,则不要删除空格。

这里应该标记为正确的答案是严格按照JLS关于您所给出的示例所说的答案。这个答案有点推测性,但它首先要观察int c:发生了什么

int c = a+++b;

这被评估为:

int c = (a++) + b; // c == 6

因此,编译器似乎正在分配后缀运算符,该运算符具有非常高的优先级。现在:

int d = a+++++b;

如果我们也试图形成后缀运算符,我们会遇到问题:

int d = (a++)++ + b;

上面的内容不会编译,因为我们不能将++应用于除基元之外的任何内容。因此,这在编译时失败。

我可以从@Daniel那里看到答案,他引用JLS的话说,每一步都使用尽可能长的翻译,这意味着在添加之前会尝试后缀,因为后者更长。这与d表达式似乎正在发生的情况一致。

解析器将使用尽可能多的字符:

  • a+++b被解析为a, ++, +, b
  • a+++++b解析为a, ++, ++, +, b

前者是有效语法,但后者不是。

最新更新