下面的程序在相同的范围内具有两个名字相同的声明,并且没有任何明显的方式可以在它们
二者之间做选择。这个程序会打印 Black 吗?它会打印 White 吗?甚至,它是合法的吗?
public class ShadesOfGray { public static void main(String[] args){ System.out.println(X.Y.Z); } } class X { static class Y { static String Z = "Black"; } static C Y = new C(); } class C { String Z = "White"; }
没有任何显而易见的方法可以确定该程序应该打印 Black 还是 White。编译器通
常会拒绝模棱两可的程序,而这个程序看起来肯定是模棱两可的。因此,它似乎
应该是非法的。如果你试着运行它,就会发现它是合法的,并且会打印出 White。
你怎样才能事先了解这一切呢?
可以证明,在这样的上下文环境中,有一条规则决定着程序的行为,即当一个变
量和一个类型具有相同的名字,并且它们位于相同的作用域时,变量名具有优先
权[JLS 6.5.2]。变量名将遮掩(obscure)类型名[JLS 6.3.2]。相似地,变量
名和类型名可以遮掩包名。这条规则真的是相当地晦涩,任何依赖于它的程序都
极有可能使它的读者晕头转向。
幸运的是,遵守标准的 Java 命名习惯的程序继续从来都不会遇上这个问题。类
应该以一个大写字母开头,以 MixedCase 的形式书写;变量应该以一个小写字母
开头,以 mixedCase 的形式书写;而常量应该以一个大写字母开头,以 ALL_CAPS
的方式书写。单个的大写字母只能用于类型参数,就像在泛型接口 Map<K,V>中
那样。包名应该以 lower.case 的方式命名[JLS 6.8]。
为了避免常量名与类名的冲突,在类名中应该将首字母缩拼词当作普通的词处理
[EJ Item 38]。例如,一个表示全局唯一标识符的类应该被命名为 Uuid,而不
是 UUID,尽管其首字母缩拼词通常被写为 UUID。(Java 平台库就违反了这项建
议,因为它具有 UUID、URL 和 URI 这样的类名。)为了避免变量名与包名的冲突,
请不要使用顶层的包名或领域名作为变量的名字,特别是不要将一个变量命名为
com、org、net、edu、java 或 javax。
要想移除 ShadesOfGray 这个程序中的所有不明确性,只需以遵守命名习惯的方
式对其重写即可。很明显,下面的程序将打印 Black。作为一种附加的好处,当
你大声朗读这个程序时,听起来还最初的那个程序是完全一样的。
public class ShadesOfGray { public static void main(String[ ] args){ System.out.println(Ex.Why.Z); } } class Ex { static class Why { static String Z = "Black"; } static See y = new See(); } class See { String Z = "White"; }
总之,应该遵守标准的命名习惯以避免不同的命名空间之间的冲突,还有一个原
因就是如果你违反这些习惯,那么你的程序将让人难以辨认。同样,为了避免变
量名与通用的顶层包名相冲突,请使用 MixedCase 风格的类名,即使其名字是首
字母缩拼词也应如此。通过遵守这些规则,你就可以确保你的程序永远不会遮掩
类名或包名。再次说明一下,这里列举的仍然是你应该在覆写之外的情况中避免
名字重用的一个实例。对语言设计者来说,应该考虑去消除遮掩的可能性。C#
是通过将域和嵌套类置于相同的命名空间来实现这一点的。