在Java中,我可以声明一个变量,其名称与其类名完全相同。我认为这是一个令人困惑和奇怪的设计。
所以我在下面的代码片段中遇到了一个问题:编译器如何区分ClassName
,它引用了变量名称或类名称?
在运行结果中,编译器将ClassName
引用为变量名。
class ClassName{}
public class Test {
public static void main(String[] args){
ClassName ClassName = new ClassName();
System.out.println(ClassName); //ClassName@18fb53f6
}
}
答案 0 :(得分:17)
编译器可以通过上下文来判断。在您给出的示例中:
ClassName ClassName = new ClassName();
1 2 3
可以看到 1 是类型名称应该是的位置,因此它知道你的意思是类。然后, 2 是期望变量名称的位置,因此它知道这应该是变量的名称。并且 3 在带有括号的new
关键字之后,因此它必须是类的名称。
System.out.println( ClassName );
在这种情况下,ClassName
处于参数传递的上下文中。类型名称不能作为参数传递,因此您必须表示变量的名称。
为了娱乐自己,您可以将print语句更改为:
System.out.println( ClassName.class );
将鼠标光标悬停在ClassName
上,您将看到编译器将其识别为类的名称。然后将其更改为:
System.out.println( ClassName.getClass() );
再次悬停光标,现在您看到它将其识别为变量名称。这是因为.class
只能应用于类型名称,而getClass()
只能应用于对象引用。在两种情况下,print语句的结果都是相同的 - 但是通过不同的机制。
所以编译器在这里没有问题。但你是对的,它对人类来说是不可读的。惯例是变量和方法的名称必须以小写字母开头,而类型名称必须以大写字母开头。遵守这一惯例将确保不会出现这种可读性问题。
我不能确切地说为什么Java的作者选择不强制执行这种约定(即,如果类型名称以小写字母开头或变量/方法名称以大写字母开头,则会产生编译器错误),但我推测他们不想做任何实际的错误,除非它实际上会导致编译器的歧义。编译错误应该表明编译器无法完成其工作的问题。
答案 1 :(得分:2)
编译器如何区分" Classname"
因为有两个组件:变量类型和变量名称。您声明类型为ClassName
的变量ClassName
。类型总是先行。类不是第一类对象(意味着你不能引用类),除非你进入反射(使用.class
属性)。
因此,在print语句中:
System.out.println(ClassName);
那只能是变量。 System.out.println
采用对象引用,并且您有一个名为ClassName
的变量引用的对象,因此编译器可以解析它。
我能想到的唯一一个对编译器不明确的情况是,如果变量引用的对象具有与该类上的静态方法同名的实例方法。
public class SomeClass {
public void aMethod() {
System.out.println("A method!");
}
public static void aMethod() {
System.out.println("Static version!");
}
}
public class TestClass {
public static void main (String[] args) {
SomeClass SomeClass = new SomeClass();
SomeClass.aMethod(); // does this call the instance method or the static method?
}
}
我确信编译器会检测歧义并以某种特定方式处理它(在Java规范中)。可能是以下之一:
如果最后2个中的任何一个,我想会记录编译器警告。
现在编译器问题暂时搁置,代码的唯一其他消费者就是人类。编制者可能能够依靠规范来保证理性行为,但人类不能。我们很容易混淆。我对此的最好建议就是,不要这样做!
绝对没有理由将变量命名为与类相同。实际上,我见过的大多数Java编码样式约定都使用lowerCamelCase命名变量和方法,使用UpperCamelCase命名类,因此除非你偏离标准,否则它们无法发生冲突。
如果我在我正在处理的项目中遇到类似的代码,我会在做其他任何事情之前立即重命名变量。
对于我的同名实例和静态方法的模糊情况,那里也可能有人类的教训:不要这样做!
Java有很多规则迫使你做一些合乎逻辑的事情并使代码易于遵循,但最终,它仍然是代码,你可以编写你想要的任何代码。没有语言规范或编译器可以阻止您编写令人困惑的代码。
答案 2 :(得分:2)
ClassName ClassName = new ClassName();
如果你学习编译器设计课程,你会知道有一个词法分析步骤。在这一步,您将为您的语言编写语法。例如:
ClassName variableName = new ClassName();
上面的示例,编译器可以理解第二 ClassName 是变量。
当您执行以下操作时:
ClassName.doSomething();
Java会将ClassName理解为变量而不是类。而这种设计不会有任何限制。 doSomething()
既可以是静态方法,也可以只是实例方法。
如果Java在这里将ClassName理解为类,那么doSomething()
不能是实例方法。也许是因为这样Java创建者选择了上面的设计:ClassName作为变量。
但是如果变量名不能与他们的类同名,那么问题是什么。以下示例:
ClassA ClassB = new ClassA();
ClassB.callMethodInClassB(); // should compile error or not ???!!!
问题仍然存在。误导仍然存在。所以新设计应该是:
No variable name should not has same name with **any** class name.
你会看到,这句话使得一种语言更容易理解而且定义不太明确。从上面的证据中,我认为当你做一些事情时:A A = new A();
理解A作为变量是语言设计的最佳方式。
希望这有帮助:)