下面的程序在相同的范围内具有两个名字相同的声明,并且没有任何明显的方式可以在它们二者之间做选择。这个程序会打印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中那样。包名应该以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(). }