为什么不强制执行Java命名约定?

3

Java有强制的类、方法、字段和变量命名规范。

  • 类名应以大写字母开头
  • 方法、字段和变量名应以小写字母开头

JDK对这两个规则只有很少的例外。

但是约定并不是语法规则,因此像下面的类编译时不会出错(C#程序员可能会很高兴):

 public class string {
      private char[] C;
      public int Length() { return C.length; }
 }

但是,传统与语法规则之间的差距不可避免地会导致初学者违反惯例,然后需要对命名惯例进行漫长的解释。
如果最基本的命名约定(如上述约定)成为语法的一部分,则Java编译器将强制执行它们并自动教育那些初学者。
所以问题来了:
从Java语言设计师的角度来看,留下语法和命名惯例之间的差距是否有任何好的理由,这些惯例永远不应该被违反?是否存在任何有意义的使用案例,涉及类、方法、字段、变量的命名,这些名称违反了约定,但超越了公约的意义?

4
因为它们是惯例。 - user207421
1
我可以想到一种违反规范的用例:代码混淆。(当然,大多数混淆器在字节码级别工作,而您可能是在谈论源代码。) - Ted Hopp
为什么我需要一条语法规则,而不是让语言变得明确无歧义?显然你从未进行过编译器设计或语言设计。你不需要任意的规则,你需要消除歧义的规则。编程语言的历史非常清楚地表明了这一点。 - user207421
1
  1. @EJP 对不起,我的问题并不涉及到我对编译器设计或编译器设计的一般知识。我只是想问为什么一个永远不应该被违反的命名约定不能成为语法的一部分,这样不是更好吗?
  2. 有时候,如果编译器告诉新手她使用了错误的类名,比让某个强烈意见的同事来指出要好得多。
- wero
1
我不明白你的“对不起”。问题是关于编译器设计和语言设计,没有别的。命名约定不是语法规则;而语法规则是由从事此项工作的人精心设计的,以使其不冗余。否则,您很容易就会得到一个自相矛盾的语言规范。但是,对于你的问题来说,真正的答案是现在问已经太晚了。向后兼容性就是这样。 - user207421
显示剩余4条评论
5个回答

3
约定俗成是在语言定义之后编写的,因此可以进行后期改进而不会破坏兼容性。约定的问题在于它们涉及品味。例如,使用空格或制表符,将$用作变量名,以m__开头的字段名称等,甚至是否在getter和setter中添加getset(我更喜欢不添加)。
Java实际上允许您做一些事情,这可能会使C程序员感到不适。我不知道为什么他们允许这样做,但我认为他们不想通过强加比实际需要更多的规则来限制采纳率。
请注意,由于使用了可能不应该允许但确实允许的字符,此Java代码片段是有效的。
for (char c‮h = 0; c‮h < Character.MAX_VALUE; c‮h++)
    if (Character.isJavaIdentifierPart(c‮h) && !Character.isJavaIdentifierStart(c‮h))
        System.out.printf("%04x <%s>%n", (int) c‮h, "" + c‮h);

大多数集成开发环境(IDE)都会帮助初学者编写符合约定的代码。唯一的问题是,大多数开发人员不知道如何充分利用他们的IDE。;)


不破坏现有代码是一个论点。但是Gosling和他的同事从一开始就强制执行惯例。为什么他们不把它作为语法规则呢? - wero
@wero 实际上在JDK中仍有一些类没有遵循规范。它是Hashtable而不是HashTable。甚至Java 6也存在明显的错误,如hashcode而不是hashCode。ByteBuffer没有将getset添加到getter和setter的开头,这是我喜欢的风格。 - Peter Lawrey
@wero 在Java 8中唯一强制执行的约定是你不能有一个名为$的lambda变量,但你可以使用$$_ - Peter Lawrey
@bayou.io 正确,我把 $__ 弄反了,它们没问题,但不要用 _ - Peter Lawrey

2

个人认为不强制规范的原因很简单,因为从技术上讲并不真正需要。例如,在Java中,您必须将类文件命名为与类完全相同的名称,因为Java类加载器无法加载其他名称的类文件。将这些检查内置到编译器中会使源代码膨胀,并且正如其名称所示,编译器通过解析源文件并检查语法将源代码转换为机器码/字节码/任何其他形式。检查类是否以大写或小写字母开头根本不是编译器的工作。

当然,编程语言通过不强制执行此类约定为您提供一定程度的自由,以便根据语言的语法规则样式化代码。


编译器检查名称的语法规则。例如,Java类标识符不能以数字开头。让它检查Java类以大写字符开头不是什么大问题... - wero
1
@wero 这不是语法规则,也不是“强制执行”的。这是一个词汇规则,用于确定标识符的起始和结束位置。 - user207421

2
从Java语言设计者的角度来看:是否有任何好的理由在语法和命名约定之间留下一个永远不应该违反的差距?
是的。 "永远"是一个强烈的词。
该语言有要求和建议。标识符的语言规范 是一个要求。但是那些强烈的命名约定是建议。
对于编译器识别它们为标记,有一些标识符的定义是必要的。将该定义留得比正常情况下更松散,让我们在超出正常情况的情况下具有一定自由度。
是否有任何有意义的用例可以用于违反约定但超越约定的名称(类,方法,字段,变量)?
是的。Java程序可以与其他具有不同约定的语言交互。 代码转换 有时候,当从另一种语言手动转换代码时,保留原始大小写更容易理解。 代码生成 有时候我们从未为Java编写过的规范生成代码。例如,我们可能会从WSDL文件生成代码,或使用SWIG生成包装器。 代码包装器 一些Java方法可以包装外部函数。例如,JNA允许使用本机函数的名称和签名定义接口。 JVM语言 多种语言可以在Java虚拟机上运行。这些其他语言有自己的约定。将不同语言混合在一个程序中是可能的。走出常规可能是相互交互所必需的。

有趣的参数。但是交互可能会要求例如以数字开头的类名,因此必须有其他手段实现互操作性。 - wero
1
没错。但是大多数编程语言不支持以数字开头的标识符。尽管我们在规范之外的一小部分情况下能够获得自由,即使在这一小部分情况中我们无法获得很小一部分的好处。 :) - Andy Thomas

2

如果我在标识符中使用了一些中文字符,它们就没有大小写区分:)因此,惯例并不总是能够被执行。

当然,可以很有把握地说99.9%的Java代码都是英文的。您也可以认为只有某些字符集才能强制执行这种规定。

我同意这种命名约定已变得至关重要,应该严格遵循。不遵循约定的Java源代码实际上是难以理解的。


很好的提醒,大小写是文化问题。但也许我们可以为中文标识符制定一个例外规则... - wero

1
我想这就是为什么它只是一种约定而不是规则......我不明白为什么应该强制执行,还有许多其他未被强制执行的约定(例如,在其他方法之前放置构造函数,在公共方法之前放置私有方法等等),这将过于严格(至少在我看来)。 我可以想到一个情况,你不希望强制执行这个约定 - 通常也会将const变量写成大写 - 再次,这只是一种约定。
无论如何,我认为在大多数IDE中,您可以配置它在违反此类约定时发出警告。这可能会对您有所帮助。

好的论点:有许多值得争议的约定,你不能强制执行它们所有。但是我为最基本的约定提出了一个案例,例如大写/小写命名规则。 - wero
2
在我看来,规范应该让代码对人类可读,语法应该让编译器能够读懂。只要编译器可以在没有规范的情况下“理解”您的代码-那就是一种约定,并不需要强制执行。 - Nir Levy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接