【问题标题】:Why variable names in Java cannot have same names as keywords?为什么Java中的变量名不能与关键字同名?
【发布时间】:2014-10-10 07:44:30
【问题描述】:

在我知道的大多数编程语言中,您不能声明一个名称也是关键字的变量。

例如在 Java 中:

public class SomeClass
{
    Class<?> clazz = Integer.class; // OK.
    Class<?> class = Integer.class; // Compilation error.
}

但是很容易弄清楚什么是什么。阅读它的人不会混淆变量名和类声明,编译器也很可能不会混淆它。

如果我们谈论的是 Java 编程语言,变量名称也一样,例如“for”、“extends”、“goto”或Java key words 中的任何内容。

我们有这个限制的原因是什么?

【问题讨论】:

  • 好评论,这听起来像是一个答案。那么是编译速度比的原因吗?
  • 这只是一种语言设计选择。 Java 采用了明确、清晰的语法,因为这使代码更具可读性。当然,其他语言以可读性为代价做出了不同的选择。
  • 不仅仅是速度。它简化了编译器代码并提高了其可靠性。此外,肯定有一些案例几乎不可能绝对确定地解释。
  • 您可以轻松构建编译器不唯一的示例: 采用以下(有效)代码:while(true); 这是带有签名while(boolean)while 循环的方法吗?
  • 你为什么想要一种语言,可以让源读者产生歧义

标签: java parsing syntax compiler-construction


【解决方案1】:

它允许词法分析器在不必消除上下文歧义的情况下对符号进行分类 - 这反过来又允许根据语法规则解析语言不需要了解编译的其他(“更高”)部分过程,包括类型分析。

作为消除这种区别会增加解析的复杂性(和歧义)的示例,请考虑以下内容。在标准 Java 规则下,它声明并分配了一个变量——它的解析方式没有歧义。

final Foo x = 2;   // roughly: <keyword> <identifier> <identifier> = <value>

现在,在没有严格关键字区分的假设语言中,想象以下情况,其中final 可能是声明的类型;现在有两个可能的读数。第一种是当 final 不是 类型并且存在标准阅读时:

final Foo = 2;     // roughly: <keyword> <identifier> ?error? = <value>

但如果 final “最终类型”,那么读法可能是:

final Foo = 2;     // hypothetical: <identifier> <identifier> = <value>

对来源的哪种解释是正确的?

由于单独编译,Java 使这个问题更难回答。现在是否应该在(或意外导入)命名空间中添加新的“最终类型”来改变代码的解析方式?报告一个未解析的符号是一回事 - 根据这种解析改变语法的解析方式是另一回事。

通过保留字的明确区分,这些问题被简单地绕过了。


可以说,可能会有特殊的产生式改变动态识别关键字(某些语言允许可控的运算符优先级),但这在主流语言中没有做到,而是Java 肯定支持。至少它需要额外的语法并增加了系统的复杂性,而没有足够的好处。

我见过的解决此类问题的最“干净”的方法是在 C# 中,它允许使用一个前缀保留字并删除特殊含义,例如 class @class { float @int = 2; } - 尽管这种情况很少会这样做,而且很糟糕!

现在,Java 中保留的某些词可以“仅在上下文中保留”,例如extends。这种情况在 SQL 中一直可见;有保留字(例如OVER),然后是在给定语句结构中仅具有特殊含义的字(例如ROW_NUMBER)。不过说reserved就是reserved比较容易,去挑别的吧。

除了像 LISP 方言这样非常易于解析的语言,它有效地将每个裸词视为标识符,关键字以及与标识符的区别在语言语法中非常普遍。

【讨论】:

  • 如果您投反对票,请留下理由/更正。谢谢!
  • 在某些情况下(对于某些语言)编译器/解析器无法区分没有任何进一步的注释。
  • JVM 确实允许您使用关键字名称作为变量名称、方法名称甚至类名称来加载类。这引起了很多问题,尤其是在 Minecraft 中,混淆器会创建诸如 do/for 之类的类名。可以在此处找到此类类的示例生成器。 pastebin.com/ybzVJFRT
  • @Smith_61 但是,什么时候可以与源代码中的混淆名称进行交互? (我不怀疑这是个问题,但我从未处理过 minecraft 源或模组。)
  • 一些模组制作者没有使用 MCP 或其他反混淆类的工具,当他们不得不使用已重命名为关键字的类时,这就成了一个问题。编译器应该对这些名称感到窒息,但它们对于字节码中的类来说是完全有效的名称。除了混淆之外,使用这些名称实际创建类没有任何好处。
【解决方案2】:

我们有这个限制的原因是什么?

一般来说有两个原因:

  • 正如您在问题中指出的那样:这会让人类读者非常困惑。而且设计上令人困惑的编程语言不会作为一种实用编程语言获得显着的吸引力。

  • 如果标识符可以与关键字相同,则为该语言编写形式语法会变得更加困难。 (当然,具有消歧规则的语法不能用 BNF / EBNF 或类似语言表达。)这意味着为这种语言编写解析器会复杂得多。

无论如何,虽然这两个原因都不是完全的“阻碍”,但它们足以让大多数尝试新的编程语言设计/实现的人拒绝这个想法。

当然是真正的原因,您(几乎)从未见过可以将关键字用作标识符的语言。编程语言设计者几乎总是拒绝这个想法......

(在 Java 的情况下,有意识地努力使习惯于 C 语言的人可以访问语法。C 不支持这一点。那将是第三个原因......如果他们正在寻找一)


在主流编程语言中有一个有趣的(半)反例。在 FORTRAN 的早期版本中,标识符中的空格并不重要。因此

    I J = 1

    IJ = 1

意思是一样的。这很酷(取决于你的“品味”......)。但是比较这两个:

    DO 20 I = 10, 1, -2

    DO 20 I = 10

一个是赋值,另一个是“DO 循环”语句。作为读者,您会注意到这一点吗?

【讨论】:

  • @Bohdan - 它是很久很久以前设计的......在经验证据表明是什么让编程语言易于阅读/使用之前。
【解决方案3】:

你不太对劲。关键字是在语言语法中有意义的词,保留字是不允许用作标识符的词。在 Java 中,它们大多是相同的,但 'true' 和 'goto' 是保留字,而不是关键字('true' 是文字,不使用 'goto')。

在语言中保留关键字的主要原因是为了简化解析并避免歧义。例如,如果return 可以是一种方法,这意味着什么?

return(1);

在我看来,Java 已经走得太远了。有些关键词只在特定的上下文中才有意义,其中不会有歧义。也许避免读者混淆是有好处的,但我把它归结为编译器编写者的习惯。还有其他语言的关键字和/或保留字要少得多,而且工作得很好。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-09-25
    • 2012-04-02
    • 1970-01-01
    • 2019-12-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-15
    相关资源
    最近更新 更多