Java 据说会忽略多余的空格。为什么 c=a++ + ++b 没有空格就不能编译?

Java is said to ignore extra whitespace. Why does c=a++ + ++b not compile without the spaces?

在所有关于 Java 的书籍中,我读到编译器以相同的方式处理所有空格并简单地忽略额外的空格,因此最佳做法是自由使用它们以提高代码的可读性。我在我写的每一个表达式中都找到了这一点的证据:有没有空格,有多少(或者也许我只是没有注意)并不重要。

最近我决定对运算符优先级和结合性进行一些实验,以测试优先级 table 的实际效果,并尝试编译

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

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

Exception in thread "main" java.lang.RuntimeException: Uncompilable source code - unexpected type. Required: variable. Found: value.

但是,当我添加空格时:int d = a++ + ++b,它编译了。为什么会这样? Java 据说无论如何都会忽略额外的空格。 (如果这很重要,我有 Java 8 和 Netbeans IDE 8.2。)

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

更新。为了解决 'extra' 重要的评论,而不是所有空格:因为 int c = a++ + b;int c=a+++b; 都可以编译,可以类比地说,在 int d = a ++ + ++b; 中空格是'extra' 还有。

语法分析器需要理解你在写什么。

如果不在加号之间放置分隔符,那么从语法分析器的角度来看,加号的序列并不稳定。

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

所以两行结果相同:

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

请考虑以下代码:

int d = a +++ b;

你的意图是什么?

int d = a + ++b;

int d = a++ + b;

此外,从人类的角度来看,没有额外的空格是无法理解的。

此外,如果此代码适用于编译器,那么从人类的角度来看,它并不不稳定。

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


无论如何,我的建议是从人性化的角度让您的代码尽可能整洁,这样它会更容易被维护、阅读和增强。因此,在需要时使用空格,不要滥用它们,但如果生成的代码可读性较差,也不要删除它们。

Java Language Specification section 3.2, "Lexical Translations", 说(强调我的):

A raw Unicode character stream is translated into a sequence of tokens, using the following three lexical translation steps, which are applied in turn:

  1. A translation of Unicode escapes [...]

  2. A translation [...] into a stream of input characters and line terminators [...].

  3. A translation of the stream of input characters and line terminators resulting from step 2 into a sequence of input elements (§3.5) which, after white space (§3.6) and comments (§3.7) are discarded, comprise the tokens (§3.5) that are the terminal symbols of the syntactic grammar (§2.3).

The longest possible translation is used at each step, even if the result does not ultimately make a correct program while another lexical translation would.

所以白色space字符被丢弃,但是"sequence of input elements"之后是确定的。第 3.5 节 "Input Elements and Tokens" 说:

White space (§3.6) and comments (§3.7) can serve to separate tokens that, if adjacent, might be tokenized in another manner. For example, the ASCII characters - and = in the input can form the operator token -= (§3.12) only if there is no intervening white space or comment.

此处应标记为正确的答案是严格按照 JLS 对您提供的示例所说的内容进行的。这个答案有点推测性,但首先要观察 int c:

会发生什么
int c = a+++b;

这被评估为:

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

所以看起来编译器正在分配具有非常高优先级的后缀运算符。现在:

int d = a+++++b;

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

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

上面的不会编译,因为我们不能将 ++ 应用到原始类型以外的任何东西。所以,这在编译时失败了。

我可以看到@Daniel 的回答,其中引用了 JLS 的说法,在每一步都使用了最长的可能翻译,这意味着将在添加之前尝试后缀,因为后者更长。这与 d 表达式似乎发生的情况一致。

解析器将消耗 as many characters as possible:

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

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